Unify the dashboard experience and retire the multi-theme engine so onboarding and day-to-day invoicing feel consistent and easier to maintain.

Shared layout, tabs, and sidebar timer; user onboarding and registration polish; settings danger zone and data export; chart and tRPC perf fixes; migrations for onboarding and dropped appearance columns.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-26 03:08:22 -04:00
co-authored by Cursor
parent 6ec26a4a0d
commit c53f2e6c4d
79 changed files with 2871 additions and 3576 deletions
+4 -102
View File
@@ -1,126 +1,28 @@
import { z } from "zod";
export const interfaceThemeValues = [
"beenvoice",
"frutiger",
"frutiger-aero",
"shadcn",
"minimal",
"editorial",
] as const;
export const fontPreferenceValues = [
"brand",
"frutiger",
"platform",
"inter",
"serif",
] as const;
export const radiusPreferenceValues = ["none", "sm", "md", "lg", "xl"] as const;
export const sidebarStyleValues = ["floating", "docked"] as const;
export const colorModeValues = ["light", "dark", "system"] as const;
export const colorThemeValues = [
"slate",
"blue",
"green",
"rose",
"orange",
"custom",
] as const;
export const pdfTemplateValues = ["classic", "minimal"] as const;
export const interfaceThemeSchema = z.enum(interfaceThemeValues);
export const fontPreferenceSchema = z.enum(fontPreferenceValues);
export const radiusPreferenceSchema = z.enum(radiusPreferenceValues);
export const sidebarStyleSchema = z.enum(sidebarStyleValues);
export const colorModeSchema = z.enum(colorModeValues);
export const colorThemeSchema = z.enum(colorThemeValues);
export const pdfTemplateSchema = z.enum(pdfTemplateValues);
export const hslChannelsSchema = z
.string()
.trim()
.regex(
/^(?:360(?:\.0)?|3[0-5]\d(?:\.\d)?|[12]?\d?\d(?:\.\d)?)\s+(?:100(?:\.0)?|\d{1,2}(?:\.\d)?)%\s+(?:100(?:\.0)?|\d{1,2}(?:\.\d)?)%$/,
"Use HSL channels like 142.1 76.2% 36.3%",
);
export type InterfaceTheme = z.infer<typeof interfaceThemeSchema>;
export type FontPreference = z.infer<typeof fontPreferenceSchema>;
export type RadiusPreference = z.infer<typeof radiusPreferenceSchema>;
export type SidebarStyle = z.infer<typeof sidebarStyleSchema>;
export type ColorMode = z.infer<typeof colorModeSchema>;
export type ColorTheme = z.infer<typeof colorThemeSchema>;
export type PdfTemplate = z.infer<typeof pdfTemplateSchema>;
export const fallbackAppearance = {
interfaceTheme: "beenvoice",
fontPreference: "brand",
bodyFontPreference: "brand",
headingFontPreference: "brand",
radiusPreference: "xl",
sidebarStyle: "floating",
colorMode: "system",
colorTheme: "slate",
customColor: undefined,
brandName: "beenvoice",
brandTagline:
"Simple and efficient invoicing for freelancers and small businesses",
brandLogoText: "beenvoice",
brandIcon: "$",
pdfTemplate: "classic",
export const defaultColorMode: ColorMode = "system";
export const defaultPdfSettings = {
pdfTemplate: "classic" as PdfTemplate,
pdfAccentColor: "#111827",
pdfFooterText: "Professional Invoicing",
pdfShowLogo: true,
pdfShowPageNumbers: true,
} satisfies {
interfaceTheme: InterfaceTheme;
fontPreference: FontPreference;
bodyFontPreference: FontPreference;
headingFontPreference: FontPreference;
radiusPreference: RadiusPreference;
sidebarStyle: SidebarStyle;
colorMode: ColorMode;
colorTheme: ColorTheme;
customColor?: string;
brandName: string;
brandTagline: string;
brandLogoText: string;
brandIcon: string;
pdfTemplate: PdfTemplate;
pdfAccentColor: string;
pdfFooterText: string;
pdfShowLogo: boolean;
pdfShowPageNumbers: boolean;
};
export function isInterfaceTheme(value: unknown): value is InterfaceTheme {
return interfaceThemeSchema.safeParse(value).success;
}
export function isFontPreference(value: unknown): value is FontPreference {
return fontPreferenceSchema.safeParse(value).success;
}
export function isColorMode(value: unknown): value is ColorMode {
return colorModeSchema.safeParse(value).success;
}
export function isColorTheme(value: unknown): value is ColorTheme {
return colorThemeSchema.safeParse(value).success;
}
export function isRadiusPreference(value: unknown): value is RadiusPreference {
return radiusPreferenceSchema.safeParse(value).success;
}
export function isSidebarStyle(value: unknown): value is SidebarStyle {
return sidebarStyleSchema.safeParse(value).success;
}
export function isPdfTemplate(value: unknown): value is PdfTemplate {
return pdfTemplateSchema.safeParse(value).success;
}
export function isHslChannels(value: unknown): value is string {
return hslChannelsSchema.safeParse(value).success;
}