planning
This commit is contained in:
Binary file not shown.
72
svar-gantt-app/src/routes/api/planning/+server.ts
Normal file
72
svar-gantt-app/src/routes/api/planning/+server.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { json } from '@sveltejs/kit';
|
||||||
|
import db from '$lib/server/db';
|
||||||
|
|
||||||
|
export async function POST({ request }) {
|
||||||
|
const body = await request.json();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (Array.isArray(body)) {
|
||||||
|
if (body.length > 1) {
|
||||||
|
// Cas d’un import CSV → remplacement total
|
||||||
|
const insert = db.prepare(`
|
||||||
|
INSERT INTO resource_planning (ressource, profil, date, disponibilite)
|
||||||
|
VALUES (@ressource, @profil, @date, @disponibilite)
|
||||||
|
`);
|
||||||
|
const clear = db.prepare(`DELETE FROM resource_planning`);
|
||||||
|
const transaction = db.transaction((data: any[]) => {
|
||||||
|
clear.run();
|
||||||
|
for (const entry of data) {
|
||||||
|
insert.run(entry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
transaction(body);
|
||||||
|
} else if (body.length === 1) {
|
||||||
|
// Cas d'une modif manuelle → update ou insert
|
||||||
|
const { ressource, profil, date, disponibilite } = body[0];
|
||||||
|
const existing = db.prepare(`
|
||||||
|
SELECT id FROM resource_planning
|
||||||
|
WHERE ressource = ? AND profil = ? AND date = ?
|
||||||
|
`).get(ressource, profil, date);
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
db.prepare(`
|
||||||
|
UPDATE resource_planning
|
||||||
|
SET disponibilite = ?
|
||||||
|
WHERE id = ?
|
||||||
|
`).run(disponibilite, existing.id);
|
||||||
|
} else {
|
||||||
|
db.prepare(`
|
||||||
|
INSERT INTO resource_planning (ressource, profil, date, disponibilite)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
`).run(ressource, profil, date, disponibilite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return json({ success: true });
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Erreur POST /api/planning', e);
|
||||||
|
return json({ success: false, error: e.message }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
const rows = db.prepare(`SELECT * FROM resource_planning`).all();
|
||||||
|
|
||||||
|
const grouped = new Map();
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
const key = `${row.ressource}|${row.profil}`;
|
||||||
|
if (!grouped.has(key)) {
|
||||||
|
grouped.set(key, {
|
||||||
|
ressource: row.ressource,
|
||||||
|
profil: row.profil,
|
||||||
|
disponibilites: {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
grouped.get(key).disponibilites[row.date] = row.disponibilite;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json(Array.from(grouped.values()));
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Papa from 'papaparse';
|
import Papa from 'papaparse';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
import { writable, get } from 'svelte/store';
|
import { writable, get } from 'svelte/store';
|
||||||
|
|
||||||
const planning = writable<any[]>([]);
|
const planning = writable<any[]>([]);
|
||||||
@ -7,6 +8,9 @@
|
|||||||
|
|
||||||
let file: File | null = null;
|
let file: File | null = null;
|
||||||
|
|
||||||
|
const saveMessage = writable('');
|
||||||
|
let timeout: ReturnType<typeof setTimeout>;
|
||||||
|
|
||||||
const joursFeries = [
|
const joursFeries = [
|
||||||
'2025-01-01', '2025-04-21', '2025-05-01', '2025-05-08', '2025-05-29',
|
'2025-01-01', '2025-04-21', '2025-05-01', '2025-05-08', '2025-05-29',
|
||||||
'2025-07-14', '2025-08-15', '2025-11-01', '2025-11-11', '2025-12-25'
|
'2025-07-14', '2025-08-15', '2025-11-01', '2025-11-11', '2025-12-25'
|
||||||
@ -30,31 +34,76 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFileUpload() {
|
function showSaveMessage(message: string = '✅ Sauvegarde enregistrée') {
|
||||||
|
saveMessage.set(message);
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(() => saveMessage.set(''), 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
const res = await fetch('/api/planning');
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
if (data.length) {
|
||||||
|
const allDates = new Set<string>();
|
||||||
|
data.forEach(row => {
|
||||||
|
Object.keys(row.disponibilites).forEach(d => allDates.add(d));
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortedDates = Array.from(allDates).sort();
|
||||||
|
dates.set(sortedDates);
|
||||||
|
planning.set(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handleFileUpload() {
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
Papa.parse(file, {
|
Papa.parse(file, {
|
||||||
header: true,
|
header: true,
|
||||||
skipEmptyLines: true,
|
skipEmptyLines: true,
|
||||||
complete: (result) => {
|
complete: async (result) => {
|
||||||
const rows = result.data as any[];
|
const rows = result.data as any[];
|
||||||
|
|
||||||
const colonnesDates = Object.keys(rows[0]).filter(k => /^\d/.test(k));
|
const colonnesDates = Object.keys(rows[0]).filter(k => /^\d/.test(k));
|
||||||
dates.set(colonnesDates);
|
dates.set(colonnesDates);
|
||||||
|
|
||||||
planning.set(rows.map(row => ({
|
const structured = rows.map(row => ({
|
||||||
ressource: row.ressource,
|
ressource: row.ressource,
|
||||||
profil: row.profil,
|
profil: row.profil,
|
||||||
disponibilites: colonnesDates.reduce((acc, date) => {
|
disponibilites: colonnesDates.reduce((acc, date) => {
|
||||||
acc[date] = parseFloat(row[date] || 0);
|
acc[date] = parseFloat(row[date] || 0);
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, number>)
|
}, {} as Record<string, number>)
|
||||||
})));
|
}));
|
||||||
|
|
||||||
|
planning.set(structured);
|
||||||
|
|
||||||
|
const payload = [];
|
||||||
|
for (const row of structured) {
|
||||||
|
for (const date of colonnesDates) {
|
||||||
|
payload.push({
|
||||||
|
ressource: row.ressource,
|
||||||
|
profil: row.profil,
|
||||||
|
date,
|
||||||
|
disponibilite: row.disponibilites[date]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await fetch('/api/planning', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
|
||||||
|
showSaveMessage();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCell(rowIndex: number, date: string, value: string) {
|
async function updateCell(rowIndex: number, date: string, value: string) {
|
||||||
const val = parseFloat(value);
|
const val = parseFloat(value);
|
||||||
if (![0, 0.5, 1].includes(val)) return;
|
if (![0, 0.5, 1].includes(val)) return;
|
||||||
|
|
||||||
@ -62,6 +111,23 @@
|
|||||||
current[rowIndex].disponibilites[date] = val;
|
current[rowIndex].disponibilites[date] = val;
|
||||||
return current;
|
return current;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const row = get(planning)[rowIndex];
|
||||||
|
|
||||||
|
await fetch('/api/planning', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify([
|
||||||
|
{
|
||||||
|
ressource: row.ressource,
|
||||||
|
profil: row.profil,
|
||||||
|
date,
|
||||||
|
disponibilite: val
|
||||||
|
}
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
showSaveMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
function totalByDate(date: string) {
|
function totalByDate(date: string) {
|
||||||
@ -125,3 +191,11 @@
|
|||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if $saveMessage}
|
||||||
|
<div
|
||||||
|
class="fixed bottom-4 right-4 bg-green-600 text-white px-4 py-2 rounded shadow-lg transition-opacity duration-300"
|
||||||
|
>
|
||||||
|
{$saveMessage}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user