Phase 1: project foundation for Alpenwerk HR
Scaffolds the Next.js 16 / TypeScript strict / Tailwind v3 app per NEXTJS_REBUILD_SUPERPROMPT.md, and implements the Foundation slice from the Phase 1 plan: - Corrected Supabase schema (supabase/schema.sql): org units, employees, history, positions, hire drafts, saved reports, audit log, reorg scenarios, role-based profiles, salary-masking view, RLS policies, auto-derivation triggers, position-number generator. - Seed script (supabase/seed.ts): ~800 realistic Austrian employees across 9 divisions / 16 departments / 35 teams, history, 8 open positions, and hr_admin/manager test accounts. - Supabase clients (lib/supabase/*), design tokens (tailwind.config.ts), format/color helpers (lib/format.ts, lib/colors.ts). - Shared UI kit (components/ui): Avatar, StatusChip, Toast, Modal, SlideOver, SegmentedControl, Lookup. - Auth (login page, Server Actions) and proxy.ts (Next 16's replacement for middleware) guarding the authenticated route group. - Shell (Sidebar, Topbar, NewHireButton stub) and the Dashboard page, reading live data via employees_directory. Employees list/detail, hire wizard, action panels, org chart, positions, reports, and audit log are deferred to later phases per the plan.
This commit is contained in:
48
components/ui/Toast.tsx
Normal file
48
components/ui/Toast.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
"use client";
|
||||
|
||||
import { createContext, useCallback, useContext, useState, type ReactNode } from "react";
|
||||
|
||||
type ToastVariant = "success" | "error" | "info";
|
||||
type ToastItem = { id: number; message: string; variant: ToastVariant };
|
||||
type ToastContextValue = { showToast: (message: string, variant?: ToastVariant) => void };
|
||||
|
||||
const ToastContext = createContext<ToastContextValue | null>(null);
|
||||
|
||||
const VARIANT_STYLES: Record<ToastVariant, string> = {
|
||||
success: "bg-success-text",
|
||||
error: "bg-danger-solid",
|
||||
info: "bg-info-text",
|
||||
};
|
||||
|
||||
export function ToastProvider({ children }: { children: ReactNode }) {
|
||||
const [toasts, setToasts] = useState<ToastItem[]>([]);
|
||||
|
||||
const showToast = useCallback((message: string, variant: ToastVariant = "success") => {
|
||||
const id = Date.now() + Math.random();
|
||||
setToasts((prev) => [...prev, { id, message, variant }]);
|
||||
setTimeout(() => setToasts((prev) => prev.filter((t) => t.id !== id)), 4000);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ToastContext.Provider value={{ showToast }}>
|
||||
{children}
|
||||
<div className="pointer-events-none fixed bottom-4 right-4 z-[100] flex flex-col gap-2">
|
||||
{toasts.map((t) => (
|
||||
<div
|
||||
key={t.id}
|
||||
role="status"
|
||||
className={`pointer-events-auto rounded px-4 py-2 text-sm font-semibold text-white shadow-lg ${VARIANT_STYLES[t.variant]}`}
|
||||
>
|
||||
{t.message}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ToastContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useToast(): ToastContextValue {
|
||||
const ctx = useContext(ToastContext);
|
||||
if (!ctx) throw new Error("useToast must be used within a ToastProvider");
|
||||
return ctx;
|
||||
}
|
||||
Reference in New Issue
Block a user