File size: 6,726 Bytes
4977db2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import PromptGarageCore
import SwiftUI

struct StatisticsView: View {
    @Bindable var model: AppModel

    var body: some View {
        ScrollView {
            VStack(alignment: .leading, spacing: 22) {
                VStack(alignment: .leading, spacing: 7) {
                    Text("Statistik")
                        .font(.largeTitle.weight(.semibold))
                    Text("Ein lokaler Blick auf deine Sammlung — ohne Sync- oder Cloud-Annahmen.")
                        .foregroundStyle(.secondary)
                }

                LazyVGrid(columns: [GridItem(.adaptive(minimum: 175), spacing: 12)], spacing: 12) {
                    MetricCard(title: "Gesamt", value: model.statistics.total, systemImage: "tray.full")
                    MetricCard(title: "Favoriten", value: model.statistics.starred, systemImage: "star.fill")
                    MetricCard(title: "Verwendungen", value: model.statistics.totalUses, systemImage: "arrow.up.forward.circle")
                }

                if model.statistics.total == 0 && !model.isLoadingStatistics {
                    ContentUnavailableView {
                        Label("Noch keine Statistik", systemImage: "chart.bar.xaxis")
                    } description: {
                        Text("Sobald Prompts vorhanden sind, erscheinen hier Quellen und Bewertungen.")
                    }
                    .frame(maxWidth: .infinity, minHeight: 260)
                } else {
                    VStack(alignment: .leading, spacing: 13) {
                        Text("Quellenfamilien")
                            .font(.headline)
                        ForEach(model.statistics.sourceFamilies.prefix(10)) { source in
                            CountBar(
                                name: source.name.capitalized,
                                count: source.count,
                                maximum: model.statistics.sourceFamilies.first?.count ?? 1,
                                tint: AppUI.accent
                            )
                        }
                    }
                    .statisticSection()

                    VStack(alignment: .leading, spacing: 13) {
                        Text("Bewertungen")
                            .font(.headline)
                        ForEach(Array((0...5).reversed()), id: \.self) { rating in
                            CountBar(
                                name: rating == 0 ? "Unbewertet" : String(repeating: "★", count: rating),
                                count: model.statistics.ratingDistribution[rating, default: 0],
                                maximum: model.statistics.ratingDistribution.values.max() ?? 1,
                                tint: rating == 0 ? .secondary : AppUI.accent
                            )
                        }
                    }
                    .statisticSection()
                }
            }
            .padding(24)
            .frame(maxWidth: 760)
            .frame(maxWidth: .infinity)
        }
        .navigationTitle("Statistik")
        .overlay {
            if model.isLoadingStatistics {
                LoadingOverlay(label: "Statistik berechnen …")
            }
        }
        .task {
            await model.refreshStatistics()
        }
    }
}

struct StatisticsDetailView: View {
    @Bindable var model: AppModel

    var body: some View {
        ScrollView {
            VStack(alignment: .leading, spacing: 24) {
                VStack(alignment: .leading, spacing: 6) {
                    Text("Top Tags")
                        .font(.title2.weight(.semibold))
                    Text("Bestehende Tags, rein lokal gezählt.")
                        .font(.callout)
                        .foregroundStyle(.secondary)
                }

                if model.statistics.tags.isEmpty {
                    ContentUnavailableView {
                        Label("Keine Tags", systemImage: "tag")
                    } description: {
                        Text("Tagge Prompts, um Themen in deiner Sammlung sichtbar zu machen.")
                    }
                    .frame(minHeight: 240)
                } else {
                    ForEach(model.statistics.tags.prefix(20)) { tag in
                        CountBar(
                            name: tag.name,
                            count: tag.count,
                            maximum: model.statistics.tags.first?.count ?? 1,
                            tint: .blue
                        )
                    }
                }

                Divider()

                VStack(alignment: .leading, spacing: 12) {
                    Text("Modelle")
                        .font(.headline)
                    ForEach(model.statistics.models.prefix(10)) { modelName in
                        HStack {
                            Text(modelName.name)
                                .lineLimit(1)
                            Spacer()
                            Text(modelName.count.formatted())
                                .font(.callout.monospacedDigit())
                                .foregroundStyle(.secondary)
                        }
                    }
                }
            }
            .padding(24)
            .frame(maxWidth: 660, alignment: .leading)
            .frame(maxWidth: .infinity)
        }
        .navigationTitle("Sammlung")
    }
}

private struct CountBar: View {
    let name: String
    let count: Int
    let maximum: Int
    let tint: Color

    private var fraction: Double {
        guard maximum > 0 else { return 0 }
        return min(1, Double(count) / Double(maximum))
    }

    var body: some View {
        VStack(spacing: 6) {
            HStack {
                Text(name)
                    .lineLimit(1)
                Spacer()
                Text(count.formatted())
                    .font(.callout.monospacedDigit())
                    .foregroundStyle(.secondary)
            }
            GeometryReader { proxy in
                ZStack(alignment: .leading) {
                    Capsule().fill(.quaternary)
                    Capsule()
                        .fill(tint)
                        .frame(width: max(3, proxy.size.width * fraction))
                }
            }
            .frame(height: 6)
        }
    }
}

private extension View {
    func statisticSection() -> some View {
        padding(18)
            .background(.regularMaterial, in: RoundedRectangle(cornerRadius: AppUI.cardCornerRadius))
            .overlay {
                RoundedRectangle(cornerRadius: AppUI.cardCornerRadius)
                    .stroke(Color(nsColor: .separatorColor).opacity(0.6))
            }
    }
}