feat: Gruen ist die Vorgabe fuer Kategorien und Schlagwoerter
Die Farbauswahl der App lag dreimal gleich im Code; sie steht jetzt einmal in Farbwahl, mit dem Markengruen an erster Stelle. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -409,7 +409,7 @@ class CategoryGroup(Base):
|
|||||||
name: Mapped[str] = mapped_column(String(120), nullable=False)
|
name: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||||
kind: Mapped[CategoryKind] = mapped_column(_enum(CategoryKind), nullable=False, default=CategoryKind.expense)
|
kind: Mapped[CategoryKind] = mapped_column(_enum(CategoryKind), nullable=False, default=CategoryKind.expense)
|
||||||
icon: Mapped[str] = mapped_column(String(40), nullable=False, default="list")
|
icon: Mapped[str] = mapped_column(String(40), nullable=False, default="list")
|
||||||
color: Mapped[str] = mapped_column(String(9), nullable=False, default="#6b7480")
|
color: Mapped[str] = mapped_column(String(9), nullable=False, default="#3f9c72")
|
||||||
position: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
position: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||||
is_builtin: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
is_builtin: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
|
||||||
@@ -431,7 +431,7 @@ class Category(Base):
|
|||||||
name: Mapped[str] = mapped_column(String(120), nullable=False)
|
name: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||||
type: Mapped[CategoryKind] = mapped_column(_enum(CategoryKind), nullable=False, default=CategoryKind.expense)
|
type: Mapped[CategoryKind] = mapped_column(_enum(CategoryKind), nullable=False, default=CategoryKind.expense)
|
||||||
icon: Mapped[str] = mapped_column(String(40), nullable=False, default="more-horizontal")
|
icon: Mapped[str] = mapped_column(String(40), nullable=False, default="more-horizontal")
|
||||||
color: Mapped[str] = mapped_column(String(9), nullable=False, default="#6b7480")
|
color: Mapped[str] = mapped_column(String(9), nullable=False, default="#3f9c72")
|
||||||
position: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
position: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||||
is_builtin: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
is_builtin: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
is_archived: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
is_archived: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
@@ -1312,7 +1312,7 @@ class Tag(Base):
|
|||||||
ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True
|
ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True
|
||||||
)
|
)
|
||||||
name: Mapped[str] = mapped_column(String(60), nullable=False)
|
name: Mapped[str] = mapped_column(String(60), nullable=False)
|
||||||
color: Mapped[str] = mapped_column(String(9), nullable=False, default="#6b7480")
|
color: Mapped[str] = mapped_column(String(9), nullable=False, default="#3f9c72")
|
||||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ class TagOut(BaseModel):
|
|||||||
class TagCreate(BaseModel):
|
class TagCreate(BaseModel):
|
||||||
name: str = Field(min_length=1, max_length=60)
|
name: str = Field(min_length=1, max_length=60)
|
||||||
# Ohne Angabe dasselbe Grau wie bei einer neuen Kategorie.
|
# Ohne Angabe dasselbe Grau wie bei einer neuen Kategorie.
|
||||||
color: str = "#6b7480"
|
color: str = "#3f9c72"
|
||||||
|
|
||||||
|
|
||||||
class TagUpdate(BaseModel):
|
class TagUpdate(BaseModel):
|
||||||
@@ -406,7 +406,7 @@ class CategoryGroupCreate(BaseModel):
|
|||||||
name: str = Field(min_length=1, max_length=120)
|
name: str = Field(min_length=1, max_length=120)
|
||||||
kind: CategoryKind = CategoryKind.expense
|
kind: CategoryKind = CategoryKind.expense
|
||||||
icon: str = "list"
|
icon: str = "list"
|
||||||
color: str = "#6b7480"
|
color: str = "#3f9c72"
|
||||||
|
|
||||||
|
|
||||||
class CategoryGroupUpdate(BaseModel):
|
class CategoryGroupUpdate(BaseModel):
|
||||||
@@ -420,7 +420,7 @@ class CategoryCreate(BaseModel):
|
|||||||
group_id: int
|
group_id: int
|
||||||
name: str = Field(min_length=1, max_length=120)
|
name: str = Field(min_length=1, max_length=120)
|
||||||
icon: str = "more-horizontal"
|
icon: str = "more-horizontal"
|
||||||
color: str = "#6b7480"
|
color: str = "#3f9c72"
|
||||||
excluded_from_stats: bool = False
|
excluded_from_stats: bool = False
|
||||||
spending_is_good: bool = False
|
spending_is_good: bool = False
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,16 @@ extension Color {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Die Farbauswahl für Kategorien, Schlagwörter und Konten – eine Liste für
|
||||||
|
/// alle drei Formulare, damit sie nicht auseinanderlaufen. Das Markengrün
|
||||||
|
/// steht vorn und ist die Vorbelegung; der Rest bleibt bunt, sonst liessen
|
||||||
|
/// sich die Kategorien in Diagrammen nicht auseinanderhalten.
|
||||||
|
enum Farbwahl {
|
||||||
|
static let standard = "#3f9c72"
|
||||||
|
static let palette = [standard, "#3f51b5", "#b3541e", "#8e24aa",
|
||||||
|
"#c62828", "#0277bd", "#6b7480", "#5d4037"]
|
||||||
|
}
|
||||||
|
|
||||||
/// Übersetzt die Symbolnamen aus der Datenbank in SF Symbols.
|
/// Übersetzt die Symbolnamen aus der Datenbank in SF Symbols.
|
||||||
///
|
///
|
||||||
/// Die Weboberfläche benutzt Lucide-Namen und hält dafür ein eigenes
|
/// Die Weboberfläche benutzt Lucide-Namen und hält dafür ein eigenes
|
||||||
|
|||||||
@@ -183,11 +183,6 @@ struct SymbolUndFarbeFeld: View {
|
|||||||
"film", "music", "wifi", "phone", "gift", "briefcase", "refresh-ccw",
|
"film", "music", "wifi", "phone", "gift", "briefcase", "refresh-ccw",
|
||||||
]
|
]
|
||||||
|
|
||||||
private static let farben = [
|
|
||||||
"#3f51b5", "#2e7d55", "#b3541e", "#8e24aa", "#c62828",
|
|
||||||
"#0277bd", "#6b7480", "#5d4037",
|
|
||||||
]
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
auswahl(Self.symbole, aktuell: symbol) { symbol = $0 } inhalt: { name in
|
auswahl(Self.symbole, aktuell: symbol) { symbol = $0 } inhalt: { name in
|
||||||
@@ -196,7 +191,7 @@ struct SymbolUndFarbeFeld: View {
|
|||||||
.foregroundStyle(Color(hex: farbe))
|
.foregroundStyle(Color(hex: farbe))
|
||||||
}
|
}
|
||||||
|
|
||||||
auswahl(Self.farben, aktuell: farbe) { farbe = $0 } inhalt: { wert in
|
auswahl(Farbwahl.palette, aktuell: farbe) { farbe = $0 } inhalt: { wert in
|
||||||
Circle().fill(Color(hex: wert)).frame(width: 22, height: 22)
|
Circle().fill(Color(hex: wert)).frame(width: 22, height: 22)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ struct KategorieFormular: View {
|
|||||||
|
|
||||||
@State private var name = ""
|
@State private var name = ""
|
||||||
@State private var symbol = "more-horizontal"
|
@State private var symbol = "more-horizontal"
|
||||||
@State private var farbe = "#6b7480"
|
@State private var farbe = Farbwahl.standard
|
||||||
@State private var gruppeId = 0
|
@State private var gruppeId = 0
|
||||||
@State private var istSparen = false
|
@State private var istSparen = false
|
||||||
@State private var ohneAuswertung = false
|
@State private var ohneAuswertung = false
|
||||||
@@ -203,7 +203,7 @@ struct GruppenFormular: View {
|
|||||||
@State private var name = ""
|
@State private var name = ""
|
||||||
@State private var art: Kategorieart = .expense
|
@State private var art: Kategorieart = .expense
|
||||||
@State private var symbol = "list"
|
@State private var symbol = "list"
|
||||||
@State private var farbe = "#6b7480"
|
@State private var farbe = Farbwahl.standard
|
||||||
@State private var fragtNachLoeschen = false
|
@State private var fragtNachLoeschen = false
|
||||||
@State private var vorbelegt = false
|
@State private var vorbelegt = false
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,6 @@ struct TagsAnsicht: View {
|
|||||||
@State private var bearbeitet: Schlagwort?
|
@State private var bearbeitet: Schlagwort?
|
||||||
@State private var neuerName = ""
|
@State private var neuerName = ""
|
||||||
|
|
||||||
/// Dieselbe Auswahl wie im Symbol- und Farbfeld, damit die Etiketten zu
|
|
||||||
/// den Kategorien passen.
|
|
||||||
private static let farben = ["#3f51b5", "#2e7d55", "#b3541e", "#8e24aa",
|
|
||||||
"#c62828", "#0277bd", "#6b7480", "#5d4037"]
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
Section {
|
Section {
|
||||||
@@ -77,7 +72,7 @@ struct TagsAnsicht: View {
|
|||||||
let name = neuerName.trimmingCharacters(in: .whitespacesAndNewlines)
|
let name = neuerName.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
guard !name.isEmpty else { return }
|
guard !name.isEmpty else { return }
|
||||||
// Reihum eine Farbe, damit nicht alle grau sind. Ändern geht danach.
|
// Reihum eine Farbe, damit nicht alle grau sind. Ändern geht danach.
|
||||||
let farbe = Self.farben[zustand.nachschlag.schlagwoerter.count % Self.farben.count]
|
let farbe = Farbwahl.palette[zustand.nachschlag.schlagwoerter.count % Farbwahl.palette.count]
|
||||||
let geglueckt = await vorgang.fuehreAus {
|
let geglueckt = await vorgang.fuehreAus {
|
||||||
_ = try await client.legeTagAn(TagAnlegen(name: name, color: farbe))
|
_ = try await client.legeTagAn(TagAnlegen(name: name, color: farbe))
|
||||||
}
|
}
|
||||||
@@ -104,12 +99,9 @@ struct TagFormular: View {
|
|||||||
@Environment(\.dismiss) private var schliesse
|
@Environment(\.dismiss) private var schliesse
|
||||||
@State private var vorgang = Speichervorgang()
|
@State private var vorgang = Speichervorgang()
|
||||||
@State private var name = ""
|
@State private var name = ""
|
||||||
@State private var farbe = "#6b7480"
|
@State private var farbe = Farbwahl.standard
|
||||||
@State private var vorbelegt = false
|
@State private var vorbelegt = false
|
||||||
|
|
||||||
private static let farben = ["#3f51b5", "#2e7d55", "#b3541e", "#8e24aa",
|
|
||||||
"#c62828", "#0277bd", "#6b7480", "#5d4037"]
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
Form {
|
Form {
|
||||||
@@ -119,7 +111,7 @@ struct TagFormular: View {
|
|||||||
Section {
|
Section {
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
ForEach(Self.farben, id: \.self) { wert in
|
ForEach(Farbwahl.palette, id: \.self) { wert in
|
||||||
Button { farbe = wert } label: {
|
Button { farbe = wert } label: {
|
||||||
Circle()
|
Circle()
|
||||||
.fill(Color(hex: wert))
|
.fill(Color(hex: wert))
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ export default function Categories() {
|
|||||||
editing,
|
editing,
|
||||||
form: editing
|
form: editing
|
||||||
? { name: editing.name, kind: editing.kind, icon: editing.icon, color: editing.color }
|
? { name: editing.name, kind: editing.kind, icon: editing.icon, color: editing.color }
|
||||||
: { name: "", kind: "expense", icon: "list", color: "#3f51b5" },
|
: { name: "", kind: "expense", icon: "list", color: "#3f9c72" },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function openCategory(groupId, editing = null) {
|
function openCategory(groupId, editing = null) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { useToast } from "../toast";
|
|||||||
//
|
//
|
||||||
// Gesetzt werden Tags nicht hier, sondern an der Buchung – in der Liste und im
|
// Gesetzt werden Tags nicht hier, sondern an der Buchung – in der Liste und im
|
||||||
// Bearbeiten-Dialog. Hier geht es nur um den Vorrat.
|
// Bearbeiten-Dialog. Hier geht es nur um den Vorrat.
|
||||||
const STANDARDFARBE = "#3f51b5";
|
const STANDARDFARBE = "#3f9c72";
|
||||||
|
|
||||||
export default function Tags() {
|
export default function Tags() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|||||||
Reference in New Issue
Block a user