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:
Guido
2026-10-24 17:51:49 +02:00
co-authored by Claude Opus 5
parent a079bf406c
commit 2d1d571b17
8 changed files with 24 additions and 27 deletions
+3 -3
View File
@@ -409,7 +409,7 @@ class CategoryGroup(Base):
name: Mapped[str] = mapped_column(String(120), nullable=False)
kind: Mapped[CategoryKind] = mapped_column(_enum(CategoryKind), nullable=False, default=CategoryKind.expense)
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)
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)
type: Mapped[CategoryKind] = mapped_column(_enum(CategoryKind), nullable=False, default=CategoryKind.expense)
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)
is_builtin: 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
)
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)
__table_args__ = (
+3 -3
View File
@@ -361,7 +361,7 @@ class TagOut(BaseModel):
class TagCreate(BaseModel):
name: str = Field(min_length=1, max_length=60)
# Ohne Angabe dasselbe Grau wie bei einer neuen Kategorie.
color: str = "#6b7480"
color: str = "#3f9c72"
class TagUpdate(BaseModel):
@@ -406,7 +406,7 @@ class CategoryGroupCreate(BaseModel):
name: str = Field(min_length=1, max_length=120)
kind: CategoryKind = CategoryKind.expense
icon: str = "list"
color: str = "#6b7480"
color: str = "#3f9c72"
class CategoryGroupUpdate(BaseModel):
@@ -420,7 +420,7 @@ class CategoryCreate(BaseModel):
group_id: int
name: str = Field(min_length=1, max_length=120)
icon: str = "more-horizontal"
color: str = "#6b7480"
color: str = "#3f9c72"
excluded_from_stats: bool = False
spending_is_good: bool = False
+10
View File
@@ -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.
///
/// Die Weboberfläche benutzt Lucide-Namen und hält dafür ein eigenes
+1 -6
View File
@@ -183,11 +183,6 @@ struct SymbolUndFarbeFeld: View {
"film", "music", "wifi", "phone", "gift", "briefcase", "refresh-ccw",
]
private static let farben = [
"#3f51b5", "#2e7d55", "#b3541e", "#8e24aa", "#c62828",
"#0277bd", "#6b7480", "#5d4037",
]
var body: some View {
VStack(alignment: .leading, spacing: 12) {
auswahl(Self.symbole, aktuell: symbol) { symbol = $0 } inhalt: { name in
@@ -196,7 +191,7 @@ struct SymbolUndFarbeFeld: View {
.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)
}
}
+2 -2
View File
@@ -25,7 +25,7 @@ struct KategorieFormular: View {
@State private var name = ""
@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 istSparen = false
@State private var ohneAuswertung = false
@@ -203,7 +203,7 @@ struct GruppenFormular: View {
@State private var name = ""
@State private var art: Kategorieart = .expense
@State private var symbol = "list"
@State private var farbe = "#6b7480"
@State private var farbe = Farbwahl.standard
@State private var fragtNachLoeschen = false
@State private var vorbelegt = false
+3 -11
View File
@@ -11,11 +11,6 @@ struct TagsAnsicht: View {
@State private var bearbeitet: Schlagwort?
@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 {
List {
Section {
@@ -77,7 +72,7 @@ struct TagsAnsicht: View {
let name = neuerName.trimmingCharacters(in: .whitespacesAndNewlines)
guard !name.isEmpty else { return }
// 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 {
_ = try await client.legeTagAn(TagAnlegen(name: name, color: farbe))
}
@@ -104,12 +99,9 @@ struct TagFormular: View {
@Environment(\.dismiss) private var schliesse
@State private var vorgang = Speichervorgang()
@State private var name = ""
@State private var farbe = "#6b7480"
@State private var farbe = Farbwahl.standard
@State private var vorbelegt = false
private static let farben = ["#3f51b5", "#2e7d55", "#b3541e", "#8e24aa",
"#c62828", "#0277bd", "#6b7480", "#5d4037"]
var body: some View {
NavigationStack {
Form {
@@ -119,7 +111,7 @@ struct TagFormular: View {
Section {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 12) {
ForEach(Self.farben, id: \.self) { wert in
ForEach(Farbwahl.palette, id: \.self) { wert in
Button { farbe = wert } label: {
Circle()
.fill(Color(hex: wert))
+1 -1
View File
@@ -187,7 +187,7 @@ export default function Categories() {
editing,
form: editing
? { 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) {
+1 -1
View File
@@ -17,7 +17,7 @@ import { useToast } from "../toast";
//
// Gesetzt werden Tags nicht hier, sondern an der Buchung – in der Liste und im
// Bearbeiten-Dialog. Hier geht es nur um den Vorrat.
const STANDARDFARBE = "#3f51b5";
const STANDARDFARBE = "#3f9c72";
export default function Tags() {
const { t } = useTranslation();