Reorganize into monorepo layout, move mitarbeiter-app to legacy reference
Consolidates the previously separate omsorgapp and omsorgCore repos (each had their own nested .git with GitHub history) plus the old root-level website/mitarbeiter-app into a single monorepo, matching the structure already documented in the root CLAUDE.md. Also moves the PHP employee app aside as omsorgWeb/mitarbeiter-app-legacy/ to serve as a template for a ground-up rewrite. Fixes .gitignore in the same pass: the config-secrets/uploads/data patterns were unanchored (relative to repo root, not depth-agnostic), so they silently stopped matching once the app moved under omsorgWeb/. Patterns are now **/-prefixed and cover both mitarbeiter-app and mitarbeiter-app-legacy, keeping DB/SMTP credentials and uploaded employee documents out of version control. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
8beb0fcf52
commit
b6c1389c55
@@ -0,0 +1,52 @@
|
||||
import { Bell, LogOut, Search, UserCircle2 } from "lucide-react";
|
||||
import { useAuth } from "../app/AuthContext";
|
||||
|
||||
export default function Header() {
|
||||
const { user, logout } = useAuth();
|
||||
|
||||
return (
|
||||
<header className="header">
|
||||
|
||||
<div className="header-left">
|
||||
|
||||
<h1>Guten Morgen Sabina & Malik ☀️</h1>
|
||||
|
||||
<p>
|
||||
Schön, dass ihr heute wieder da seid.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="header-right">
|
||||
|
||||
<button className="icon-button">
|
||||
<Search size={20} />
|
||||
</button>
|
||||
|
||||
<button className="icon-button notification">
|
||||
|
||||
<Bell size={20} />
|
||||
|
||||
<span className="notification-dot">
|
||||
3
|
||||
</span>
|
||||
|
||||
</button>
|
||||
|
||||
<button className="icon-button">
|
||||
<UserCircle2 size={24} />
|
||||
</button>
|
||||
|
||||
{user?.username && (
|
||||
<span className="header-username">{user.username}</span>
|
||||
)}
|
||||
|
||||
<button className="icon-button" title="Abmelden" onClick={logout}>
|
||||
<LogOut size={20} />
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
</header>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* ==========================================================
|
||||
* OMSORG BUSINESS CONTROLS PRO
|
||||
* Component: OmsorgCard
|
||||
* Sprint: 0.2.0
|
||||
* ==========================================================
|
||||
*/
|
||||
|
||||
export default function OmsorgCard({
|
||||
title,
|
||||
value,
|
||||
subtitle,
|
||||
children,
|
||||
}) {
|
||||
return (
|
||||
<section className="omsorg-card">
|
||||
{title && (
|
||||
<div className="omsorg-card-header">
|
||||
<h3>{title}</h3>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{value && (
|
||||
<div className="omsorg-card-value">
|
||||
{value}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{subtitle && (
|
||||
<p className="omsorg-card-subtitle">
|
||||
{subtitle}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import logo from "../assets/omsorg_logo.png";
|
||||
import {
|
||||
Home,
|
||||
Users,
|
||||
Building2,
|
||||
CalendarDays,
|
||||
Calculator,
|
||||
Car,
|
||||
FileText,
|
||||
BarChart3,
|
||||
Settings,
|
||||
Bug
|
||||
} from "lucide-react";
|
||||
import { useAuth } from "../app/AuthContext";
|
||||
import { isNavItemVisible } from "../app/navPermissions";
|
||||
|
||||
const menu = [
|
||||
{ icon: Home, label: "Home" },
|
||||
{ icon: Users, label: "Mitarbeiter" },
|
||||
{ icon: Building2, label: "Kunden" },
|
||||
{ icon: CalendarDays, label: "Disposition" },
|
||||
{ icon: Calculator, label: "Kalkulation" },
|
||||
{ icon: Car, label: "Fahrzeuge" },
|
||||
{ icon: FileText, label: "Rechnungen" },
|
||||
{ icon: BarChart3, label: "Controlling" },
|
||||
{ icon: Settings, label: "Einstellungen" },
|
||||
{ icon: Bug, label: "Debug" }
|
||||
];
|
||||
|
||||
export default function Sidebar({
|
||||
activePage,
|
||||
onNavigate
|
||||
}) {
|
||||
const { hasPermission } = useAuth();
|
||||
const items = menu.filter((item) => isNavItemVisible(item.label, hasPermission));
|
||||
|
||||
return (
|
||||
|
||||
<aside className="sidebar">
|
||||
|
||||
<div className="sidebar-logo">
|
||||
|
||||
<img
|
||||
src={logo}
|
||||
alt="Omsorg"
|
||||
/>
|
||||
|
||||
<div>
|
||||
|
||||
<h2>OMSORG</h2>
|
||||
|
||||
<p>
|
||||
Business Controls Pro
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
|
||||
{items.map((item) => {
|
||||
|
||||
const Icon = item.icon;
|
||||
|
||||
const active =
|
||||
activePage === item.label;
|
||||
|
||||
return (
|
||||
|
||||
<button
|
||||
|
||||
key={item.label}
|
||||
|
||||
className={
|
||||
active
|
||||
? "menu-button active"
|
||||
: "menu-button"
|
||||
}
|
||||
|
||||
onClick={() =>
|
||||
onNavigate(item.label)
|
||||
}
|
||||
|
||||
>
|
||||
|
||||
<Icon size={20} />
|
||||
|
||||
<span>{item.label}</span>
|
||||
|
||||
</button>
|
||||
|
||||
);
|
||||
|
||||
})}
|
||||
|
||||
</nav>
|
||||
|
||||
</aside>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import {
|
||||
CheckCircle2,
|
||||
BriefcaseMedical,
|
||||
Palmtree,
|
||||
HeartPulse,
|
||||
Clock3,
|
||||
} from "lucide-react";
|
||||
|
||||
const badgeConfig = {
|
||||
aktiv: {
|
||||
label: "Aktiv",
|
||||
icon: CheckCircle2,
|
||||
className: "success",
|
||||
},
|
||||
|
||||
einsatz: {
|
||||
label: "Einsatz",
|
||||
icon: BriefcaseMedical,
|
||||
className: "info",
|
||||
},
|
||||
|
||||
urlaub: {
|
||||
label: "Urlaub",
|
||||
icon: Palmtree,
|
||||
className: "warning",
|
||||
},
|
||||
|
||||
krank: {
|
||||
label: "Krank",
|
||||
icon: HeartPulse,
|
||||
className: "danger",
|
||||
},
|
||||
|
||||
vertrag: {
|
||||
label: "Läuft aus",
|
||||
icon: Clock3,
|
||||
className: "neutral",
|
||||
},
|
||||
};
|
||||
|
||||
export default function OmsorgBadge({
|
||||
status = "aktiv",
|
||||
}) {
|
||||
|
||||
const config =
|
||||
badgeConfig[status] ?? badgeConfig.aktiv;
|
||||
|
||||
const Icon = config.icon;
|
||||
|
||||
return (
|
||||
<span
|
||||
className={`omsorg-badge omsorg-badge--${config.className}`}
|
||||
>
|
||||
<Icon size={14} />
|
||||
{config.label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* ==========================================================
|
||||
* OMSORG BUSINESS CONTROLS PRO
|
||||
* Component: OmsorgButton
|
||||
* Sprint: 0.2.0
|
||||
* ==========================================================
|
||||
*/
|
||||
|
||||
export default function OmsorgButton({
|
||||
children,
|
||||
variant = "primary",
|
||||
icon: Icon,
|
||||
type = "button",
|
||||
disabled = false,
|
||||
onClick,
|
||||
}) {
|
||||
|
||||
const className =
|
||||
`omsorg-button omsorg-button--${variant}`;
|
||||
|
||||
return (
|
||||
|
||||
<button
|
||||
type={type}
|
||||
className={className}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
>
|
||||
|
||||
{Icon && <Icon size={18} />}
|
||||
|
||||
<span>
|
||||
|
||||
{children}
|
||||
|
||||
</span>
|
||||
|
||||
</button>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
|
||||
import OmsorgButton from "./OmsorgButton";
|
||||
|
||||
export default function OmsorgPagination({ page, totalPages, onPageChange }) {
|
||||
return (
|
||||
<div className="omsorg-pagination">
|
||||
<OmsorgButton
|
||||
variant="secondary"
|
||||
icon={ChevronLeft}
|
||||
disabled={page <= 1}
|
||||
onClick={() => onPageChange(page - 1)}
|
||||
>
|
||||
Zurück
|
||||
</OmsorgButton>
|
||||
|
||||
<span className="omsorg-pagination-status">
|
||||
Seite {page} von {totalPages}
|
||||
</span>
|
||||
|
||||
<OmsorgButton
|
||||
variant="secondary"
|
||||
icon={ChevronRight}
|
||||
disabled={page >= totalPages}
|
||||
onClick={() => onPageChange(page + 1)}
|
||||
>
|
||||
Weiter
|
||||
</OmsorgButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
ArrowDownRight,
|
||||
ArrowUpRight,
|
||||
Minus,
|
||||
} from "lucide-react";
|
||||
|
||||
export default function OmsorgStatCard({
|
||||
title,
|
||||
value,
|
||||
subtitle,
|
||||
trend = "neutral",
|
||||
icon: Icon,
|
||||
}) {
|
||||
const TrendIcon =
|
||||
trend === "up"
|
||||
? ArrowUpRight
|
||||
: trend === "down"
|
||||
? ArrowDownRight
|
||||
: Minus;
|
||||
|
||||
return (
|
||||
<article className="omsorg-stat-card">
|
||||
<div className="omsorg-stat-card__top">
|
||||
<div>
|
||||
<p className="omsorg-stat-card__title">
|
||||
{title}
|
||||
</p>
|
||||
|
||||
<strong className="omsorg-stat-card__value">
|
||||
{value}
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
{Icon && (
|
||||
<div className="omsorg-stat-card__icon">
|
||||
<Icon size={22} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{subtitle && (
|
||||
<div
|
||||
className={`omsorg-stat-card__trend omsorg-stat-card__trend--${trend}`}
|
||||
>
|
||||
<TrendIcon size={16} />
|
||||
<span>{subtitle}</span>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user