v0.1.0 — Phase β-0 Setup
Repo-Skelett für cards-native, native SwiftUI-Universal-App für Cardecky (mana e.V.). Web-Parität zu cardecky.mana.how. - project.yml mit Bundle ev.mana.cards, ManaSwiftCore-Dep via path - AppConfig: auth.mana.how + cardecky-api.mana.how, Keychain ev.mana.cards - CardsTheme: forest-Werte aus mana/packages/themes/.../forest.css - LoginView (Email/PW gegen mana-auth via ManaCore.AuthClient) - DashboardView als β-1-Placeholder mit cardecky-api-Reachability-Probe - Log unter Subsystem ev.mana.cards - 3 AppConfig-Tests - iOS-Simulator-Build grün Phasen-Plan: mana/docs/playbooks/CARDS_NATIVE_GREENFIELD.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
28b20cd934
21 changed files with 896 additions and 0 deletions
78
Sources/Features/Account/LoginView.swift
Normal file
78
Sources/Features/Account/LoginView.swift
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import ManaCore
|
||||
import SwiftUI
|
||||
|
||||
struct LoginView: View {
|
||||
@Environment(AuthClient.self) private var auth
|
||||
@State private var email = ""
|
||||
@State private var password = ""
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
CardsTheme.background.ignoresSafeArea()
|
||||
VStack(spacing: 24) {
|
||||
Text("Cards")
|
||||
.font(.system(size: 48, weight: .bold))
|
||||
.foregroundStyle(CardsTheme.primary)
|
||||
Text("Karteikarten des Vereins mana e.V.")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(CardsTheme.mutedForeground)
|
||||
|
||||
VStack(spacing: 12) {
|
||||
TextField("Email", text: $email)
|
||||
.textContentType(.emailAddress)
|
||||
.keyboardType(.emailAddress)
|
||||
.textInputAutocapitalization(.never)
|
||||
.autocorrectionDisabled()
|
||||
.padding(.vertical, 12)
|
||||
.padding(.horizontal, 16)
|
||||
.background(CardsTheme.surface, in: RoundedRectangle(cornerRadius: 8))
|
||||
|
||||
SecureField("Passwort", text: $password)
|
||||
.textContentType(.password)
|
||||
.padding(.vertical, 12)
|
||||
.padding(.horizontal, 16)
|
||||
.background(CardsTheme.surface, in: RoundedRectangle(cornerRadius: 8))
|
||||
}
|
||||
.padding(.horizontal, 32)
|
||||
|
||||
Button {
|
||||
Task { await auth.signIn(email: email, password: password) }
|
||||
} label: {
|
||||
HStack {
|
||||
if case .signingIn = auth.status {
|
||||
ProgressView()
|
||||
.controlSize(.small)
|
||||
.tint(CardsTheme.primaryForeground)
|
||||
}
|
||||
Text("Anmelden")
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 14)
|
||||
.background(CardsTheme.primary, in: RoundedRectangle(cornerRadius: 8))
|
||||
.foregroundStyle(CardsTheme.primaryForeground)
|
||||
}
|
||||
.padding(.horizontal, 32)
|
||||
.disabled(isSigningIn || email.isEmpty || password.isEmpty)
|
||||
|
||||
if case let .error(message) = auth.status {
|
||||
Text(message)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(CardsTheme.error)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 32)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var isSigningIn: Bool {
|
||||
if case .signingIn = auth.status { return true }
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
LoginView()
|
||||
.environment(AuthClient(config: AppConfig.manaAppConfig))
|
||||
}
|
||||
58
Sources/Features/Decks/DashboardView.swift
Normal file
58
Sources/Features/Decks/DashboardView.swift
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import ManaCore
|
||||
import SwiftUI
|
||||
|
||||
/// Phase β-0-Placeholder. Wird in β-1 durch eine echte Tab-Bar mit
|
||||
/// Decks / Study / Stats / Account ersetzt.
|
||||
struct DashboardView: View {
|
||||
@Environment(AuthClient.self) private var auth
|
||||
@State private var apiReachable: Bool?
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
CardsTheme.background.ignoresSafeArea()
|
||||
VStack(spacing: 24) {
|
||||
Text("Cards")
|
||||
.font(.largeTitle.bold())
|
||||
.foregroundStyle(CardsTheme.primary)
|
||||
|
||||
if let email = auth.currentEmail {
|
||||
Text("Angemeldet als \(email)")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(CardsTheme.mutedForeground)
|
||||
}
|
||||
|
||||
ContentUnavailableView {
|
||||
Label("β-1 in Vorbereitung", systemImage: "rectangle.stack")
|
||||
.foregroundStyle(CardsTheme.foreground)
|
||||
} description: {
|
||||
Text("Decks- und Study-Views kommen in der nächsten Phase.")
|
||||
.foregroundStyle(CardsTheme.mutedForeground)
|
||||
}
|
||||
|
||||
if let reachable = apiReachable {
|
||||
Label(
|
||||
reachable ? "cardecky-api erreichbar" : "cardecky-api nicht erreichbar",
|
||||
systemImage: reachable ? "checkmark.circle.fill" : "xmark.circle.fill"
|
||||
)
|
||||
.foregroundStyle(reachable ? CardsTheme.success : CardsTheme.error)
|
||||
.font(.footnote)
|
||||
}
|
||||
|
||||
Button("Abmelden", role: .destructive) {
|
||||
Task { await auth.signOut() }
|
||||
}
|
||||
.padding(.top, 24)
|
||||
}
|
||||
.padding(32)
|
||||
}
|
||||
.task {
|
||||
let api = CardsAPI(auth: auth)
|
||||
apiReachable = (try? await api.healthCheck()) ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
DashboardView()
|
||||
.environment(AuthClient(config: AppConfig.manaAppConfig))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue