mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-11 17:26:32 +02:00
feat: restrict gamme/export/exports-page to admin; fix create user button style
- Non-admin users see gamme as read-only badge (no select) - Export button hidden for non-admin in grid - Save drafts button hidden for non-admin - Exports page hidden in sidebar for non-admin - Create user button: explicit blue style (was unreadable with apple-btn-primary) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
f52532343f
commit
ead53db496
4 files changed
+29
-7
No files matched your search
@@ -14,7 +14,7 @@ const NAV_ITEMS = [
|
||||
{ icon: TrendingUp, label: "Analytics", href: "/analytics" },
|
||||
{ icon: Trophy, label: "Hit Parade", href: "/hit-parade" },
|
||||
{ icon: Camera, label: "Snapshots", href: "/snapshots" },
|
||||
{ icon: FileDown, label: "Exports", href: "/exports" },
|
||||
{ icon: FileDown, label: "Exports", href: "/exports", adminOnly: true },
|
||||
{ icon: Settings, label: "Paramètres", href: "/settings", adminOnly: true },
|
||||
];
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ export function UserManagement() {
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isCreating}
|
||||
className="apple-btn-primary w-full justify-center"
|
||||
className="flex items-center gap-2 justify-center w-full px-4 py-2.5 rounded-xl bg-blue-600 hover:bg-blue-500 disabled:opacity-50 text-white text-sm font-semibold transition-colors"
|
||||
>
|
||||
{isCreating ? <Loader2 className="w-4 h-4 animate-spin" /> : <Check className="w-4 h-4" />}
|
||||
Créer le compte
|
||||
|
||||
@@ -10,6 +10,7 @@ import { useGridStore } from "@/features/grid/store/use-grid-store";
|
||||
import { useSaveDrafts } from "@/features/grid/hooks/use-save-drafts";
|
||||
import type { ProductRow } from "@/types/grid";
|
||||
import { CheckCircle, AlertCircle, Loader2 } from "lucide-react";
|
||||
import { useSession } from "next-auth/react";
|
||||
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { computeProductScores } from "@/lib/score-engine";
|
||||
@@ -24,6 +25,8 @@ interface GridClientProps {
|
||||
}
|
||||
|
||||
export function GridClient({ initialRows, codeFournisseur, nomFournisseur, fournisseurs, magasins, magasin }: GridClientProps) {
|
||||
const { data: session } = useSession();
|
||||
const isAdmin = (session?.user as any)?.role === "admin";
|
||||
const setRows = useGridStore((s) => s.setRows);
|
||||
const setActiveGridQuery = useGridStore((s) => s.setActiveGridQuery);
|
||||
const setFilter = useGridStore((s) => s.setFilter);
|
||||
@@ -98,8 +101,8 @@ export function GridClient({ initialRows, codeFournisseur, nomFournisseur, fourn
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div id="grid-toolbar-actions"></div>
|
||||
<ExportDropdown nomFournisseur={nomFournisseur} />
|
||||
{hasDrafts && (
|
||||
{isAdmin && <ExportDropdown nomFournisseur={nomFournisseur} />}
|
||||
{isAdmin && hasDrafts && (
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={isPending}
|
||||
@@ -141,6 +144,7 @@ export function GridClient({ initialRows, codeFournisseur, nomFournisseur, fourn
|
||||
<div className="flex-1 min-h-0 min-w-0">
|
||||
<HeatmapGrid
|
||||
onSelectionChange={setSelectedCodeins}
|
||||
isAdmin={isAdmin}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ function getStoreConfig(name: string) {
|
||||
|
||||
interface HeatmapGridProps {
|
||||
onSelectionChange?: (codeins: string[]) => void;
|
||||
isAdmin?: boolean;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
@@ -90,7 +91,7 @@ interface HeatmapGridProps {
|
||||
// =========================================================================
|
||||
|
||||
// 1. Composant isolé pour la Cellule Gamme (évite le re-render des colonnes)
|
||||
const GammeCell = React.memo(({ row }: { row: ProductRow }) => {
|
||||
const GammeCell = React.memo(({ row, isAdmin }: { row: ProductRow; isAdmin?: boolean }) => {
|
||||
// Abonnement ultra-ciblé : la cellule ne re-render que si SA valeur change
|
||||
const codein = row.codein;
|
||||
const isDraft = useGridStore((s) => s.draftChanges[codein] !== undefined);
|
||||
@@ -98,6 +99,23 @@ const GammeCell = React.memo(({ row }: { row: ProductRow }) => {
|
||||
const setDraftGamme = useGridStore((s) => s.setDraftGamme);
|
||||
|
||||
const isModified = row.codeGamme !== row.codeGammeInit && row.codeGammeInit !== null;
|
||||
const displayValue = (!effectiveGamme || effectiveGamme.trim() === "") ? "Aucune" : effectiveGamme;
|
||||
|
||||
if (!isAdmin) {
|
||||
return (
|
||||
<div className={[
|
||||
"w-full text-xs font-bold rounded-lg py-1.5 px-2 text-center",
|
||||
displayValue === "A" ? "bg-emerald-500/10 text-emerald-700" :
|
||||
displayValue === "B" ? "bg-blue-500/10 text-blue-700" :
|
||||
displayValue === "C" ? "bg-amber-500/10 text-amber-700" :
|
||||
displayValue === "Y" ? "bg-violet-500/10 text-violet-700" :
|
||||
displayValue === "Z" ? "bg-rose-500/10 text-rose-700" :
|
||||
"bg-slate-500/10 text-slate-500"
|
||||
].join(" ")}>
|
||||
{displayValue === "Aucune" ? "—" : displayValue}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative group/gamme">
|
||||
@@ -249,7 +267,7 @@ function CellDetailModal({ d, activeMagasin, onClose }: { d: CellDetailData; act
|
||||
|
||||
// =========================================================================
|
||||
|
||||
export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
|
||||
export function HeatmapGrid({ onSelectionChange, isAdmin }: HeatmapGridProps) {
|
||||
// L'abonnement doit être minimal ici ! PAS de draftChanges ni de setDraftGamme.
|
||||
const { rows, filters, displayDensity, draftChanges, activeMagasin } = useGridStore();
|
||||
|
||||
@@ -626,7 +644,7 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
|
||||
id: "gamme",
|
||||
header: () => <div className="text-center w-full">Gamme</div>,
|
||||
size: 110,
|
||||
cell: ({ row }) => <GammeCell row={row.original} />,
|
||||
cell: ({ row }) => <GammeCell row={row.original} isAdmin={isAdmin} />,
|
||||
},
|
||||
{
|
||||
id: "ai",
|
||||
|
||||
Reference in new issue
Block a user