import { useCollection, useIdentity } from '@deplixo/sdk';
import Dashboard from './components/Dashboard.jsx';
import Products from './components/Products.jsx';
import Menus from './components/Menus.jsx';
import Recipes from './components/Recipes.jsx';
import Clients from './components/Clients.jsx';
import Orders from './components/Orders.jsx';
import Ingredients from './components/Ingredients.jsx';
import History from './components/History.jsx';
import { Stock, Production, Reminders, Payments, Calendar, Deliveries, ShoppingList, Settings } from './components/OtherSections.jsx';
import { SEED_RECIPES } from './components/seedRecipes.js';
const NAV = [
{ key: 'home', label: 'Accueil', icon: 'home' },
{ key: 'orders', label: 'Commandes', icon: 'orders' },
{ key: 'clients', label: 'Clients', icon: 'users' },
{ key: 'production', label: 'Production', icon: 'check' },
{ key: 'shopping', label: 'Courses', icon: 'cart' },
{ key: 'recipes', label: 'Recettes', icon: 'book' },
{ key: 'more', label: 'Plus', icon: 'more' },
];
const MORE_ITEMS = [
{ key: 'products', label: 'Produits' },
{ key: 'menus', label: 'Menus' },
{ key: 'ingredients', label: 'Ingrédients' },
{ key: 'stock', label: 'Stock' },
{ key: 'calendar', label: 'Calendrier' },
{ key: 'payments', label: 'Paiements' },
{ key: 'deliveries', label: 'Livraisons' },
{ key: 'reminders', label: 'Rappels' },
{ key: 'history', label: 'Historique des modifications' },
{ key: 'settings', label: 'Paramètres / Administration' },
];
function Icon({ name }) {
const paths = {
home: ,
orders: ,
users: ,
check: ,
cart: ,
book: ,
more: ,
};
return (
);
}
function LoginScreen() {
return (
CROQ’UP MIGNARDISE
BY MAË
Connexion en cours…
);
}
// Fields to track per entity type. Everything else is ignored (or hidden as "autres").
const TRACKED_FIELDS = {
product: ['name', 'price', 'category', 'description', 'recipeId'],
menu: ['name', 'price', 'description', 'items'],
recipe: ['name', 'yield', 'unit', 'instructions', 'ingredients', 'notes'],
order: ['number', 'clientName', 'eventDate', 'eventTime', 'status', 'deliveryMode', 'address', 'lines', 'payments', 'notes'],
};
function diffValues(oldV, newV, fields) {
const changes = [];
for (const f of fields) {
const a = oldV ? oldV[f] : undefined;
const b = newV ? newV[f] : undefined;
const sa = JSON.stringify(a);
const sb = JSON.stringify(b);
if (sa !== sb) changes.push({ field: f, oldValue: a, newValue: b });
}
return changes;
}
function App() {
const { user, loading: identityLoading } = useIdentity();
const products = useCollection('products');
const menus = useCollection('menus');
const recipes = useCollection('recipes');
const clients = useCollection('clients');
const orders = useCollection('orders');
const ingredients = useCollection('ingredients');
const tasks = useCollection('tasks');
const reminders = useCollection('reminders');
const users = useCollection('users');
const history = useCollection('history');
const [view, setView] = useState('home');
const [focusId, setFocusId] = useState(null);
const [toast, setToast] = useState('');
const [moreOpen, setMoreOpen] = useState(false);
function notify(msg) {
setToast(msg);
setTimeout(() => setToast(''), 2500);
}
function nav(v, id) {
setView(v);
if (id) setFocusId(id);
setMoreOpen(false);
window.scrollTo(0, 0);
}
// Auto-register user on first load
useEffect(() => {
if (!user || users.loading) return;
const existing = (users.items || []).find(u => u.value?.userId === user.id);
if (!existing) {
const isFirst = (users.items || []).length === 0;
users.add({ userId: user.id, name: user.name, role: isFirst ? 'Admin' : 'Éditeur', joinedAt: new Date().toISOString() });
}
// eslint-disable-next-line
}, [user, users.loading, users.items?.length]);
const myRole = useMemo(() => {
if (!user) return 'Lecteur';
const u = (users.items || []).find(x => x.value?.userId === user.id);
return u?.value?.role || 'Admin';
}, [user, users.items]);
const canEdit = myRole === 'Admin' || myRole === 'Éditeur';
const canAdmin = myRole === 'Admin';
// --- History logging wrappers ---
function logChange({ entityType, entityId, entityName, action, changes }) {
try {
history.add({
entityType,
entityId: entityId || '',
entityName: entityName || '',
action,
changes: changes || [],
userName: user?.name || '',
userId: user?.id || '',
date: new Date().toISOString(),
});
} catch (e) { /* non-blocking */ }
}
function nameOf(entityType, value) {
if (!value) return '';
if (entityType === 'order') return `#${value.number || ''} — ${value.clientName || ''}`;
return value.name || '';
}
function trackedUpdate(collection, entityType) {
return async (id, newValue) => {
const existing = (collection.items || []).find(x => x.id === id);
const oldValue = existing?.value;
const fields = TRACKED_FIELDS[entityType] || [];
const changes = diffValues(oldValue, newValue, fields);
const result = await collection.update(id, newValue);
if (changes.length > 0) {
logChange({
entityType,
entityId: id,
entityName: nameOf(entityType, newValue) || nameOf(entityType, oldValue),
action: 'update',
changes,
});
}
return result;
};
}
function trackedAdd(collection, entityType) {
return async (value) => {
const result = await collection.add(value);
logChange({
entityType,
entityId: result?.id || '',
entityName: nameOf(entityType, value),
action: 'create',
changes: [],
});
return result;
};
}
function trackedRemove(collection, entityType) {
return async (id) => {
const existing = (collection.items || []).find(x => x.id === id);
const oldValue = existing?.value;
const result = await collection.remove(id);
logChange({
entityType,
entityId: id,
entityName: nameOf(entityType, oldValue),
action: 'delete',
changes: [],
});
return result;
};
}
const productsUpdate = trackedUpdate(products, 'product');
const productsAdd = trackedAdd(products, 'product');
const productsRemove = trackedRemove(products, 'product');
const menusUpdate = trackedUpdate(menus, 'menu');
const menusAdd = trackedAdd(menus, 'menu');
const menusRemove = trackedRemove(menus, 'menu');
const recipesUpdate = trackedUpdate(recipes, 'recipe');
const recipesAdd = trackedAdd(recipes, 'recipe');
const recipesRemove = trackedRemove(recipes, 'recipe');
const ordersUpdate = trackedUpdate(orders, 'order');
const ordersAdd = trackedAdd(orders, 'order');
const ordersRemove = trackedRemove(orders, 'order');
async function seedRecipes() {
for (const r of SEED_RECIPES) {
// Avoid duplicates
const exists = (recipes.items || []).some(x => x.value?.name === r.name);
if (!exists) await recipes.add(r);
}
notify('Recettes de base chargées');
}
if (identityLoading || !user) {
return ;
}
const anyLoading = products.loading || menus.loading || recipes.loading || clients.loading || orders.loading || ingredients.loading || tasks.loading || reminders.loading || users.loading;
const commonProps = {
products: products.items || [],
menus: menus.items || [],
recipes: recipes.items || [],
clients: clients.items || [],
orders: orders.items || [],
ingredients: ingredients.items || [],
tasks: tasks.items || [],
reminders: reminders.items || [],
canEdit,
notify,
};
return (
{view === 'home' && (
)}
{view === 'orders' && (
setFocusId(null)}
/>
)}
{view === 'clients' && (
)}
{view === 'production' && (
)}
{view === 'shopping' && (
)}
{view === 'recipes' && (
)}
{view === 'products' && (
)}
{view === 'menus' && (
)}
{view === 'ingredients' && (
)}
{view === 'stock' && (
)}
{view === 'calendar' && (
)}
{view === 'payments' && (
)}
{view === 'deliveries' && (
)}
{view === 'reminders' && (
)}
{view === 'history' && (
)}
{view === 'settings' && (
)}
{view === 'more' && (
{MORE_ITEMS.map(m => (
))}
)}
{toast &&
{toast}
}
);
}
ReactDOM.createRoot(document.getElementById('root')).render();