3 Commits

Author SHA1 Message Date
karim gabriele varano e345602dc2 macOS: Ad-hoc Signing für Bundle aktivieren
Beseitigt die "App ist beschädigt"-Meldung beim ersten Start unsignierter
Downloads. Nutzer können die App nun via Rechtsklick → Öffnen oder über
Systemeinstellungen → Datenschutz freigeben, ohne xattr-Workaround.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 03:18:02 +02:00
karim gabriele varano 4aac21e3e9 UI: Spinner statt LADEN-Text, Pills unter Header in Einstellungen
- ViewFallback: dezenter, zentrierter Spinner statt kaum lesbarem
  "LADEN…"-Text zwischen lazy-loaded Views
- Einstellungen: Pill-Navigation jetzt konsistent unter dem Header
  (zuvor lagen die Pills bei allen Tabs ausser "Mein Profil" darüber)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 03:13:58 +02:00
karim 7d069dc367 Delete public/erste-schritte.html 2026-05-13 01:47:21 +02:00
5 changed files with 36 additions and 201 deletions
-179
View File
@@ -1,179 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Erste Schritte — Rapport</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,400;0,500;1,400&family=Playfair+Display:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet" />
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #ebe7e1;
--surface: #fdfcfa;
--text: #1a1a18;
--text2: #4a4844;
--text4: #8c8880;
--border: #ddd8d0;
--accent: #b07848;
}
html { background: var(--bg); color: var(--text); font-family: 'DM Mono', monospace; font-size: 14px; line-height: 1.6; }
body { max-width: 720px; margin: 0 auto; padding: 64px 32px 120px; }
/* Header */
.site-header { margin-bottom: 64px; padding-bottom: 28px; border-bottom: 1px solid var(--border); display: flex; align-items: baseline; justify-content: space-between; }
.logo { font-size: 22px; letter-spacing: -0.02em; color: var(--text); text-decoration: none; }
.logo span { font-family: 'Playfair Display', serif; font-style: italic; font-weight: 400; font-size: 13px; color: var(--text4); margin-left: 12px; }
/* Page title */
.page-title { font-family: 'Playfair Display', serif; font-size: 42px; font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 12px; }
.page-subtitle { font-size: 13px; color: var(--text4); margin-bottom: 56px; line-height: 1.7; max-width: 500px; }
/* Steps */
.steps { display: flex; flex-direction: column; gap: 0; }
.step { display: flex; gap: 32px; padding: 32px 0; border-bottom: 1px solid var(--border); }
.step:first-child { border-top: 1px solid var(--border); }
.step-number {
font-family: 'Playfair Display', serif;
font-size: 32px;
font-weight: 400;
color: var(--border);
line-height: 1;
flex-shrink: 0;
width: 40px;
padding-top: 2px;
}
.step-content { flex: 1; }
.step-title { font-size: 15px; font-weight: 500; margin-bottom: 8px; color: var(--text); letter-spacing: 0.01em; }
.step-desc { font-size: 12px; color: var(--text2); line-height: 1.8; }
.step-fields { margin-top: 14px; display: flex; flex-direction: column; gap: 6px; }
.step-field { display: flex; align-items: baseline; gap: 12px; font-size: 11px; }
.step-field .label { color: var(--text4); letter-spacing: 0.08em; text-transform: uppercase; flex-shrink: 0; width: 160px; }
.step-field .val { color: var(--text2); }
/* Info box */
.info-box { margin-top: 56px; padding: 24px 28px; background: var(--surface); border-radius: 10px; border: 1px solid var(--border); box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 16px rgba(0,0,0,0.05); }
.info-box-title { font-size: 10px; letter-spacing: 0.12em; color: var(--text4); text-transform: uppercase; margin-bottom: 10px; }
.info-box p { font-size: 12px; color: var(--text2); line-height: 1.8; }
/* Concept tags */
.concepts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 48px; }
.concept { padding: 6px 14px; background: var(--surface); border: 1px solid var(--border); border-radius: 20px; font-size: 11px; color: var(--text2); }
/* Footer */
.site-footer { margin-top: 80px; padding-top: 24px; border-top: 1px solid var(--border); font-size: 11px; color: var(--text4); display: flex; justify-content: space-between; align-items: center; }
.site-footer a { color: var(--text4); text-decoration: none; border-bottom: 1px solid var(--border); }
.site-footer a:hover { color: var(--text2); }
@media (max-width: 600px) {
body { padding: 40px 20px 80px; }
.page-title { font-size: 32px; }
.step { gap: 20px; }
.step-number { font-size: 24px; width: 28px; }
.site-header { flex-direction: column; gap: 8px; }
}
</style>
</head>
<body>
<header class="site-header">
<a href="/" class="logo">RAPPORT <span>Studio Administration</span></a>
<span style="font-size: 10px; letter-spacing: 0.1em; color: var(--text4);">ALPHA 0.3</span>
</header>
<h1 class="page-title">Erste Schritte</h1>
<p class="page-subtitle">Rapport ist eine lokale Studio-Administrationssoftware für Architekturbüros. Alle Daten bleiben auf deinem Gerät — kein Server, kein Login.</p>
<div class="steps">
<div class="step">
<div class="step-number">1</div>
<div class="step-content">
<div class="step-title">Einstellungen konfigurieren</div>
<div class="step-desc">Bevor du beginnst, trage die Stammdaten deines Studios ein. Diese erscheinen auf allen Dokumenten und Rechnungen.</div>
<div class="step-fields">
<div class="step-field"><span class="label">Studio-Name</span><span class="val">Erscheint in der Sidebar und auf Druckdokumenten</span></div>
<div class="step-field"><span class="label">IBAN</span><span class="val">Für den QR-Einzahlungsschein auf Rechnungen (QR-IBAN empfohlen)</span></div>
<div class="step-field"><span class="label">MwSt-Satz</span><span class="val">Standardmässig 8.1 %</span></div>
<div class="step-field"><span class="label">Stundensätze</span><span class="val">Pro Rolle, werden für Honorarberechnungen verwendet</span></div>
</div>
</div>
</div>
<div class="step">
<div class="step-number">2</div>
<div class="step-content">
<div class="step-title">Ersten Kunden anlegen</div>
<div class="step-desc">Unter <strong>Personen</strong> kannst du Kunden und Kontakte verwalten. Kunden werden mit Projekten und Rechnungen verknüpft. Name und Adresse sind für Dokumente erforderlich.</div>
</div>
</div>
<div class="step">
<div class="step-number">3</div>
<div class="step-content">
<div class="step-title">Mitarbeiter erfassen</div>
<div class="step-desc">Unter <strong>Mitarbeiter</strong> legst du dein Team an. Wichtig sind Pensum, Wochenstunden und Eintrittsdatum — diese fliessen in die Zeiterfassung und Lohnabrechnung ein.</div>
</div>
</div>
<div class="step">
<div class="step-number">4</div>
<div class="step-content">
<div class="step-title">Erstes Projekt erstellen</div>
<div class="step-desc">Unter <strong>Projekte</strong> erfasst du dein Bauvorhaben. Vergib eine Projektnummer, wähle den Auftraggeber und setze den Status. Optional kannst du eine Offerte verknüpfen, um das Stundenbudget abzuleiten.</div>
<div class="step-fields">
<div class="step-field"><span class="label">SIA-Phasen</span><span class="val">Aktiviere nur die für das Projekt relevanten Phasen</span></div>
<div class="step-field"><span class="label">Abrechnungsart</span><span class="val">Stundenbasis oder Pauschal</span></div>
</div>
</div>
</div>
<div class="step">
<div class="step-number">5</div>
<div class="step-content">
<div class="step-title">Zeit erfassen</div>
<div class="step-desc">Unter <strong>Zeiterfassung</strong> buchst du Stunden auf Projekte und Phasen. Die Wochenansicht erlaubt das visuelle Planen per Drag & Drop. Der Monatssaldo zeigt jederzeit Stand bis heute.</div>
</div>
</div>
<div class="step">
<div class="step-number">6</div>
<div class="step-content">
<div class="step-title">Erste Rechnung stellen</div>
<div class="step-desc">Unter <strong>Rechnungen</strong> erstellst du Rechnungen mit Positionen, MwSt und QR-Einzahlungsschein. Rechnungen lassen sich als PDF drucken und mit einem Status versehen (Entwurf → Gesendet → Bezahlt).</div>
</div>
</div>
</div>
<div class="info-box">
<div class="info-box-title">Datenspeicherung</div>
<p>Alle Daten werden ausschliesslich lokal im Browser gespeichert (<code>localStorage</code>). Es gibt keinen Server, keine Cloud, keine Synchronisation. Erstelle regelmässig ein Backup über <strong>Einstellungen → Datenbank exportieren</strong>.</p>
</div>
<div class="concepts">
<span class="concept">Lokal gespeichert</span>
<span class="concept">Kein Login erforderlich</span>
<span class="concept">SIA 102 Honorare</span>
<span class="concept">QR-Rechnung</span>
<span class="concept">Lohnabrechnung</span>
<span class="concept">PDF-Export</span>
<span class="concept">Zeiterfassung</span>
<span class="concept">AGPL-3.0</span>
</div>
<footer class="site-footer">
<span>Rapport Alpha 0.3 — <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank">AGPL-3.0</a></span>
<a href="https://rapport.gabrielevarano.ch/">rapport.gabrielevarano.ch</a>
</footer>
</body>
</html>
+12 -12
View File
@@ -75,18 +75,6 @@ version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "rapport"
version = "0.6.0"
dependencies = [
"log",
"serde",
"serde_json",
"tauri",
"tauri-build",
"tauri-plugin-log",
]
[[package]]
name = "arrayvec"
version = "0.7.6"
@@ -2760,6 +2748,18 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rapport"
version = "0.6.0"
dependencies = [
"log",
"serde",
"serde_json",
"tauri",
"tauri-build",
"tauri-plugin-log",
]
[[package]]
name = "raw-window-handle"
version = "0.6.2"
+4 -1
View File
@@ -32,6 +32,9 @@
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
],
"macOS": {
"signingIdentity": "-"
}
}
}
+14 -1
View File
@@ -28,7 +28,20 @@ const Documents = lazy(() => import("./views/Documents.jsx"));
const PrintView = lazy(() => import("./print/PrintComponents.jsx").then(m => ({ default: m.PrintView })));
function ViewFallback() {
return <div style={{ padding: 40, color: "var(--text4)", fontSize: 12, letterSpacing: "0.08em" }}>LADEN</div>;
return (
<div style={{ display: "flex", justifyContent: "center", alignItems: "center", minHeight: "calc(100vh - 60px)" }}>
<style>{`
@keyframes vf-spin { to { transform: rotate(360deg); } }
.vf-spinner {
width: 40px; height: 40px; border-radius: 50%;
border: 2px solid rgba(0,0,0,0.08);
border-top-color: rgba(0,0,0,0.45);
animation: vf-spin 0.9s linear infinite;
}
`}</style>
<div className="vf-spinner" />
</div>
);
}
export default function App() {
+6 -8
View File
@@ -323,14 +323,6 @@ export default function Settings({ data, update, currentUser, uiZoom, setUiZoom
<div>
{ConfirmModalEl}
{tab !== "profil" && (
<div className="filter-bar" style={{ marginBottom: 20 }}>
{TABS.map(t => (
<button key={t.id} onClick={() => setTab(t.id)} className={`pill${tab === t.id ? " active" : ""}`}>{t.label}</button>
))}
</div>
)}
{/* ── Profil-Tab ── */}
{tab === "profil" && <PersonalSettings data={data} update={update} currentUser={currentUser} uiZoom={uiZoom || 1} setUiZoom={setUiZoom || (() => {})} nav={
<div className="filter-bar" style={{ marginBottom: 20 }}>
@@ -409,6 +401,12 @@ export default function Settings({ data, update, currentUser, uiZoom, setUiZoom
: null
} />
<div className="filter-bar" style={{ marginBottom: 20 }}>
{TABS.map(t => (
<button key={t.id} onClick={() => setTab(t.id)} className={`pill${tab === t.id ? " active" : ""}`}>{t.label}</button>
))}
</div>
{/* ── Tab: Studio ── */}
{tab === "studio" && (
<>