mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-11 17:26:32 +02:00
feat: add interactive client component for Hit Parade data visualization with sorting, filtering, and date range controls
This commit is contained in:
1 parent
687009dba1
commit
83d101ddc1
4 files changed
+18
-18
No files matched your search
@@ -194,16 +194,16 @@ export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) {
|
||||
<tr className="border-b-2 border-gray-200">
|
||||
<ColHeader label="Qté" sortable="qte292" k="qte292" />
|
||||
<ColHeader label="CA TTC" sortable="ca292" k="ca292" />
|
||||
<ColHeader label="Stock" sortable="stock292" k="stock292" />
|
||||
<th className="bg-blue-50/40 px-4 py-2 text-right text-xs font-semibold text-blue-500">% Marge</th>
|
||||
<ColHeader label="Stock" sortable="stock292" k="stock292" />
|
||||
<ColHeader label="Qté" sortable="qte579" k="qte579" />
|
||||
<ColHeader label="CA TTC" sortable="ca579" k="ca579" />
|
||||
<ColHeader label="Stock" sortable="stock579" k="stock579" />
|
||||
<th className="bg-violet-50/40 px-4 py-2 text-right text-xs font-semibold text-violet-500">% Marge</th>
|
||||
<ColHeader label="Stock" sortable="stock579" k="stock579" />
|
||||
<ColHeader label="Qté" sortable="qteTotal" k="qteTotal" />
|
||||
<ColHeader label="CA TTC" sortable="caTotal" k="caTotal" />
|
||||
<ColHeader label="Stock" sortable="stockTotal" k="stockTotal" />
|
||||
<th className="bg-emerald-50/40 px-4 py-2 text-right text-xs font-semibold text-emerald-600">% Marge</th>
|
||||
<ColHeader label="Stock" sortable="stockTotal" k="stockTotal" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -218,36 +218,36 @@ export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) {
|
||||
<td className={`px-4 py-2.5 text-right tabular-nums text-gray-800 ${sortKey === "ca292" ? "bg-blue-50/50 font-semibold" : ""}`}>
|
||||
{row.ca292 > 0 ? formatCA(row.ca292) : <span className="text-gray-300">—</span>}
|
||||
</td>
|
||||
<td className={`px-4 py-2.5 text-right tabular-nums text-xs ${sortKey === "stock292" ? "bg-blue-50/50 font-semibold text-blue-700" : "text-amber-600"}`}>
|
||||
{row.stock292 > 0 ? formatQte(row.stock292) : <span className="text-gray-300">—</span>}
|
||||
</td>
|
||||
<td className="px-4 py-2.5 text-right text-xs tabular-nums text-blue-500">
|
||||
{row.ca292 > 0 ? formatPct(row.ca292, row.marge292) : ""}
|
||||
</td>
|
||||
<td className={`px-4 py-2.5 text-right tabular-nums text-xs ${sortKey === "stock292" ? "bg-blue-50/50 font-semibold text-blue-700" : "text-amber-600"}`}>
|
||||
{row.stock292 > 0 ? formatQte(row.stock292) : <span className="text-gray-300">—</span>}
|
||||
</td>
|
||||
<td className={`border-l-2 border-violet-100 px-4 py-2.5 text-right tabular-nums text-gray-700 ${sortKey === "qte579" ? "bg-violet-50/50 font-semibold" : ""}`}>
|
||||
{row.qte579 > 0 ? formatQte(row.qte579) : <span className="text-gray-300">—</span>}
|
||||
</td>
|
||||
<td className={`px-4 py-2.5 text-right tabular-nums text-gray-800 ${sortKey === "ca579" ? "bg-violet-50/50 font-semibold" : ""}`}>
|
||||
{row.ca579 > 0 ? formatCA(row.ca579) : <span className="text-gray-300">—</span>}
|
||||
</td>
|
||||
<td className={`px-4 py-2.5 text-right tabular-nums text-xs ${sortKey === "stock579" ? "bg-violet-50/50 font-semibold text-violet-700" : "text-amber-600"}`}>
|
||||
{row.stock579 > 0 ? formatQte(row.stock579) : <span className="text-gray-300">—</span>}
|
||||
</td>
|
||||
<td className="px-4 py-2.5 text-right text-xs tabular-nums text-violet-500">
|
||||
{row.ca579 > 0 ? formatPct(row.ca579, row.marge579) : ""}
|
||||
</td>
|
||||
<td className={`px-4 py-2.5 text-right tabular-nums text-xs ${sortKey === "stock579" ? "bg-violet-50/50 font-semibold text-violet-700" : "text-amber-600"}`}>
|
||||
{row.stock579 > 0 ? formatQte(row.stock579) : <span className="text-gray-300">—</span>}
|
||||
</td>
|
||||
<td className={`border-l-2 border-emerald-100 px-4 py-2.5 text-right tabular-nums font-semibold text-gray-900 ${sortKey === "qteTotal" ? "bg-emerald-50/50" : ""}`}>
|
||||
{row.qteTotal > 0 ? formatQte(row.qteTotal) : <span className="text-gray-300">—</span>}
|
||||
</td>
|
||||
<td className={`px-4 py-2.5 text-right tabular-nums font-semibold text-gray-900 ${sortKey === "caTotal" ? "bg-emerald-50/50" : ""}`}>
|
||||
{row.caTotal > 0 ? formatCA(row.caTotal) : <span className="text-gray-300">—</span>}
|
||||
</td>
|
||||
<td className={`px-4 py-2.5 text-right tabular-nums text-xs font-semibold ${sortKey === "stockTotal" ? "bg-emerald-50/50 text-emerald-700" : row.stockTotal <= 0 ? "text-red-400" : "text-amber-600"}`}>
|
||||
{row.stockTotal > 0 ? formatQte(row.stockTotal) : <span className="text-red-400 font-semibold">0</span>}
|
||||
</td>
|
||||
<td className="px-4 py-2.5 text-right text-xs tabular-nums text-emerald-600">
|
||||
{row.caTotal > 0 ? formatPct(row.caTotal, row.margeTotal) : ""}
|
||||
</td>
|
||||
<td className={`px-4 py-2.5 text-right tabular-nums text-xs font-semibold ${sortKey === "stockTotal" ? "bg-emerald-50/50 text-emerald-700" : row.stockTotal <= 0 ? "text-red-400" : "text-amber-600"}`}>
|
||||
{row.stockTotal > 0 ? formatQte(row.stockTotal) : <span className="text-red-400 font-semibold">0</span>}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
@@ -258,16 +258,16 @@ export function HitParadeClient({ dateDebut, dateFin, pivotted }: Props) {
|
||||
</td>
|
||||
<td className="border-l-2 border-blue-200 px-4 py-3 text-right font-bold tabular-nums text-gray-900">{formatQte(totals.qte292)}</td>
|
||||
<td className="px-4 py-3 text-right font-bold tabular-nums text-gray-900">{formatCA(totals.ca292)}</td>
|
||||
<td className="px-4 py-3 text-right text-xs font-bold tabular-nums text-amber-600">{formatQte(totals.stock292)}</td>
|
||||
<td className="px-4 py-3 text-right text-xs tabular-nums text-blue-600">{formatPct(totals.ca292, totals.marge292)}</td>
|
||||
<td className="px-4 py-3 text-right text-xs font-bold tabular-nums text-amber-600">{formatQte(totals.stock292)}</td>
|
||||
<td className="border-l-2 border-violet-200 px-4 py-3 text-right font-bold tabular-nums text-gray-900">{formatQte(totals.qte579)}</td>
|
||||
<td className="px-4 py-3 text-right font-bold tabular-nums text-gray-900">{formatCA(totals.ca579)}</td>
|
||||
<td className="px-4 py-3 text-right text-xs font-bold tabular-nums text-amber-600">{formatQte(totals.stock579)}</td>
|
||||
<td className="px-4 py-3 text-right text-xs tabular-nums text-violet-600">{formatPct(totals.ca579, totals.marge579)}</td>
|
||||
<td className="px-4 py-3 text-right text-xs font-bold tabular-nums text-amber-600">{formatQte(totals.stock579)}</td>
|
||||
<td className="border-l-2 border-emerald-200 px-4 py-3 text-right font-bold tabular-nums text-gray-900">{formatQte(totals.qteTotal)}</td>
|
||||
<td className="px-4 py-3 text-right font-bold tabular-nums text-gray-900">{formatCA(totals.caTotal)}</td>
|
||||
<td className="px-4 py-3 text-right text-xs font-bold tabular-nums text-amber-600">{formatQte(totals.stockTotal)}</td>
|
||||
<td className="px-4 py-3 text-right text-xs tabular-nums text-emerald-700 font-semibold">{formatPct(totals.caTotal, totals.margeTotal)}</td>
|
||||
<td className="px-4 py-3 text-right text-xs font-bold tabular-nums text-amber-600">{formatQte(totals.stockTotal)}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
@@ -118,7 +118,7 @@ export default function SettingsPage() {
|
||||
const [dbError, setDbError] = useState<string | null>(null);
|
||||
const [saveStatus, setSaveStatus] = useState<"idle" | "saving" | "saved">("idle");
|
||||
const [models, setModels] = useState<OpenRouterModel[]>([]);
|
||||
const [selectedModel, setSelectedModel] = useState("google/gemini-flash-1.5");
|
||||
const [selectedModel, setSelectedModel] = useState("google/gemini-2.0-flash-001");
|
||||
const [modelsStatus, setModelsStatus] = useState<"idle" | "loading" | "ok" | "error">("idle");
|
||||
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
|
||||
@@ -13,7 +13,7 @@ const SITE_LABELS: Record<string, string> = { "292": "Frouard", "579": "Houdemon
|
||||
export async function POST(req: NextRequest) {
|
||||
const config = await getSavedDatabaseConfig();
|
||||
const apiKey = process.env.OPENROUTER_API_KEY || config?.openRouterKey;
|
||||
const model = config?.openRouterModel || "google/gemini-flash-1.5";
|
||||
const model = config?.openRouterModel || "google/gemini-2.0-flash-001";
|
||||
|
||||
if (!apiKey) {
|
||||
console.error("[AI] OPENROUTER_API_KEY is missing.");
|
||||
|
||||
@@ -36,7 +36,7 @@ const BatchAnalyzeSchema = z.object({
|
||||
export async function POST(req: NextRequest) {
|
||||
const config = await getSavedDatabaseConfig();
|
||||
const apiKey = process.env.OPENROUTER_API_KEY || config?.openRouterKey;
|
||||
const model = config?.openRouterModel || "google/gemini-flash-1.5";
|
||||
const model = config?.openRouterModel || "google/gemini-2.0-flash-001";
|
||||
|
||||
if (!apiKey) {
|
||||
console.error("[batch-analyze] API key manquante.");
|
||||
|
||||
Reference in new issue
Block a user