update front ts
This commit is contained in:
parent
bc1c92a329
commit
a6fc5bab7a
217
backend/src/utils/get-new-data.js
Normal file
217
backend/src/utils/get-new-data.js
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
const { PrismaClient } = require('@prisma/client');
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
async function incrementalSeed() {
|
||||||
|
try {
|
||||||
|
// Récupérer tous les clients
|
||||||
|
const clients = await prisma.client.findMany();
|
||||||
|
|
||||||
|
for (const client of clients) {
|
||||||
|
// Récupérer le dernier achat pour ce client
|
||||||
|
const lastPurchase = await prisma.purchase.findFirst({
|
||||||
|
where: { products: { some: { product: { clientId: client.id } } } },
|
||||||
|
orderBy: { id: 'desc' }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Récupérer les données de l'API du client
|
||||||
|
const response = await axios.get(client.url);
|
||||||
|
const apiData = response.data;
|
||||||
|
|
||||||
|
// Filtrer pour n'avoir que les nouveaux achats
|
||||||
|
const newPurchases = apiData.filter(purchase =>
|
||||||
|
!lastPurchase || purchase.id > lastPurchase.id
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const data of newPurchases) {
|
||||||
|
// 1. Create Categories
|
||||||
|
const allCategories = new Set([
|
||||||
|
...(data.customer?.categories || []),
|
||||||
|
...(data.products || []).map(p => p.product?.category).filter(Boolean)
|
||||||
|
]);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
Array.from(allCategories).map(cat =>
|
||||||
|
prisma.category.upsert({
|
||||||
|
where: { id: cat?.id || 0 },
|
||||||
|
update: {},
|
||||||
|
create: {
|
||||||
|
id: cat?.id || 0,
|
||||||
|
name: cat?.name || 'Unknown',
|
||||||
|
imageName: cat?.image_name || null,
|
||||||
|
isForPurchase: cat?.is_for_purchase || false,
|
||||||
|
position: cat?.position || null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Create Customer
|
||||||
|
if (data.customer) {
|
||||||
|
await prisma.customer.upsert({
|
||||||
|
where: { id: data.customer?.id || 0 },
|
||||||
|
update: {},
|
||||||
|
create: {
|
||||||
|
id: data.customer?.id || 0,
|
||||||
|
name: data.customer?.name || 'Unknown',
|
||||||
|
email: data.customer?.email,
|
||||||
|
language: data.customer?.language || 'fr',
|
||||||
|
isNpai: data.customer?.is_npai || false,
|
||||||
|
country: data.customer?.country,
|
||||||
|
contactFirstName: data.customer?.contact_first_name,
|
||||||
|
contactLastName: data.customer?.contact_last_name,
|
||||||
|
discr: data.customer?.discr || 'professionalCustomer',
|
||||||
|
categories: {
|
||||||
|
connect: (data.customer?.categories || []).map(cat => ({ id: cat.id }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if no products
|
||||||
|
if (!data.products || !data.products.length) {
|
||||||
|
console.log('No products found for this entry, skipping product creation');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Create States, Types, and AcquisitionModes
|
||||||
|
for (const product of data.products) {
|
||||||
|
if (product.product?.state) {
|
||||||
|
await prisma.state.upsert({
|
||||||
|
where: { id: product.product.state?.id || 1 },
|
||||||
|
update: {},
|
||||||
|
create: {
|
||||||
|
id: product.product.state?.id || 1,
|
||||||
|
name: product.product.state?.name || 'Unknown',
|
||||||
|
color: product.product.state?.color || '#000000'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (product.product?.type) {
|
||||||
|
await prisma.type.upsert({
|
||||||
|
where: { id: product.product.type?.id || 1 },
|
||||||
|
update: {},
|
||||||
|
create: {
|
||||||
|
id: product.product.type?.id || 1,
|
||||||
|
name: product.product.type?.name || 'Unknown'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (product.product?.acquisition_mode) {
|
||||||
|
await prisma.acquisitionMode.upsert({
|
||||||
|
where: { id: product.product.acquisition_mode?.id || 1 },
|
||||||
|
update: {},
|
||||||
|
create: {
|
||||||
|
id: product.product.acquisition_mode?.id || 1,
|
||||||
|
name: product.product.acquisition_mode?.name || 'Unknown',
|
||||||
|
type: product.product.acquisition_mode?.type || 'ACQUISITION'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Create Artists
|
||||||
|
for (const product of data.products) {
|
||||||
|
if (product.product?.artist) {
|
||||||
|
await prisma.artist.upsert({
|
||||||
|
where: { id: product.product.artist?.id || 0 },
|
||||||
|
update: {},
|
||||||
|
create: {
|
||||||
|
id: product.product.artist?.id || 0,
|
||||||
|
firstName: product.product.artist?.first_name,
|
||||||
|
lastName: product.product.artist?.last_name || 'Unknown',
|
||||||
|
dateBirth: product.product.artist?.date_birth,
|
||||||
|
dateDeath: product.product.artist?.date_death,
|
||||||
|
imageName: product.product.artist?.image_name,
|
||||||
|
isPhare: product.product.artist?.is_phare || false,
|
||||||
|
isCirca: product.product.artist?.is_circa || false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Create Products
|
||||||
|
await Promise.all(
|
||||||
|
data.products.map(item =>
|
||||||
|
prisma.product.upsert({
|
||||||
|
where: { code: item.product?.code || 'UNKNOWN' },
|
||||||
|
update: {},
|
||||||
|
create: {
|
||||||
|
code: item.product?.code || 'UNKNOWN',
|
||||||
|
title: item.product?.title || 'Unknown',
|
||||||
|
acquisitionPrice: item.product?.acquisition_price || 0,
|
||||||
|
dateFirstMeeting: item.product?.date_first_meeting,
|
||||||
|
dateAcquisition: item.product?.date_acquisition,
|
||||||
|
width: item.product?.width,
|
||||||
|
height: item.product?.height,
|
||||||
|
depth: item.product?.depth,
|
||||||
|
diametre: item.product?.diametre,
|
||||||
|
widthInch: item.product?.width_inch,
|
||||||
|
heightInch: item.product?.height_inch,
|
||||||
|
depthInch: item.product?.depth_inch,
|
||||||
|
diametreInch: item.product?.diametre_inch,
|
||||||
|
encadrement: item.product?.encadrement || false,
|
||||||
|
collectionPerso: item.product?.collection_perso || false,
|
||||||
|
venteDebout: item.product?.vente_debout || false,
|
||||||
|
onWall: item.product?.on_wall || false,
|
||||||
|
isMultiple: item.product?.is_multiple || false,
|
||||||
|
wantedPrice: item.product?.wanted_price,
|
||||||
|
wantedPriceMinimum: item.product?.wanted_price_minimum,
|
||||||
|
comments: item.product?.comments,
|
||||||
|
commentHistory: item.product?.comment_history,
|
||||||
|
imageName: item.product?.image_name,
|
||||||
|
year: item.product?.year,
|
||||||
|
isCirca: item.product?.is_circa || false,
|
||||||
|
artist: { connect: { id: item.product?.artist?.id || 0 } },
|
||||||
|
category: { connect: { id: item.product?.category?.id || 0 } },
|
||||||
|
type: { connect: { id: item.product?.type?.id || 1 } },
|
||||||
|
state: { connect: { id: 1 } },
|
||||||
|
acquisitionMode: { connect: { id: item.product?.acquisition_mode?.id || 1 } },
|
||||||
|
client: { connect: { id: client.id } }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 6. Create Purchase
|
||||||
|
await prisma.purchase.create({
|
||||||
|
data: {
|
||||||
|
isGroupedPurchase: data.is_grouped_purchase || false,
|
||||||
|
location: data.location,
|
||||||
|
amountTotal: data.amount_total || 0,
|
||||||
|
amountDecremented: data.amount_decremented || 0,
|
||||||
|
fromDepotVente: data.from_depot_vente || false,
|
||||||
|
customer: { connect: { id: data.customer?.id || 0 } },
|
||||||
|
acquisitionMode: data.acquisition_mode?.id ?
|
||||||
|
{ connect: { id: data.acquisition_mode.id } } :
|
||||||
|
undefined,
|
||||||
|
products: {
|
||||||
|
create: data.products.map(item => ({
|
||||||
|
product: { connect: { code: item.product?.code || 'UNKNOWN' } },
|
||||||
|
acquisitionPrice: item.acquisition_price || 0,
|
||||||
|
quantity: item.quantity || 1
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Import réussi pour l'achat ${data.id} du client ${client.name}!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`${newPurchases.length} nouveaux achats importés pour le client ${client.name}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur lors de l\'import incrémental:', error);
|
||||||
|
} finally {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { incrementalSeed };
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
incrementalSeed();
|
||||||
|
}
|
||||||
@ -16,10 +16,40 @@ import { useState } from "react"
|
|||||||
import { Eye, ArrowUpDown } from "lucide-react"
|
import { Eye, ArrowUpDown } from "lucide-react"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
|
|
||||||
|
type Product = {
|
||||||
|
title: string
|
||||||
|
width?: number
|
||||||
|
height?: number
|
||||||
|
year?: string
|
||||||
|
dateAcquisition: string
|
||||||
|
artist: {
|
||||||
|
firstName?: string
|
||||||
|
lastName: string
|
||||||
|
}
|
||||||
|
imageName?: string
|
||||||
|
client?: {
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PurchaseProduct = {
|
||||||
|
product: Product
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Purchase {
|
||||||
|
products: PurchaseProduct[]
|
||||||
|
customer: {
|
||||||
|
id: number
|
||||||
|
contactFirstName: string
|
||||||
|
contactLastName: string
|
||||||
|
}
|
||||||
|
amountTotal: number
|
||||||
|
}
|
||||||
|
|
||||||
const columns: ColumnDef<Purchase>[] = [
|
const columns: ColumnDef<Purchase>[] = [
|
||||||
{
|
{
|
||||||
id: "artwork",
|
id: "artwork",
|
||||||
accessorFn: (row) => row.products[0]?.product.title,
|
accessorFn: (row) => row.products[0]?.product.title ?? '',
|
||||||
header: ({ column }) => (
|
header: ({ column }) => (
|
||||||
<Button variant="ghost" onClick={() => column.toggleSorting()} className="w-full justify-start">
|
<Button variant="ghost" onClick={() => column.toggleSorting()} className="w-full justify-start">
|
||||||
Œuvre <ArrowUpDown className="ml-2 h-4 w-4" />
|
Œuvre <ArrowUpDown className="ml-2 h-4 w-4" />
|
||||||
@ -39,7 +69,7 @@ const columns: ColumnDef<Purchase>[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "artist",
|
id: "artist",
|
||||||
accessorFn: (row) => `${row.products[0]?.product.artist.firstName || ''} ${row.products[0]?.product.artist.lastName}`,
|
accessorFn: (row) => `${row.products[0]?.product.artist.firstName || ''} ${row.products[0]?.product.artist.lastName || ''}`,
|
||||||
header: ({ column }) => (
|
header: ({ column }) => (
|
||||||
<Button variant="ghost" onClick={() => column.toggleSorting()} className="w-full justify-start">
|
<Button variant="ghost" onClick={() => column.toggleSorting()} className="w-full justify-start">
|
||||||
Artiste <ArrowUpDown className="ml-2 h-4 w-4" />
|
Artiste <ArrowUpDown className="ml-2 h-4 w-4" />
|
||||||
@ -48,7 +78,7 @@ const columns: ColumnDef<Purchase>[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "Date d'acquisition",
|
id: "Date d'acquisition",
|
||||||
accessorFn: (row) => row.products[0]?.product.title,
|
accessorFn: (row) => row.products[0]?.product.dateAcquisition ?? '',
|
||||||
header: ({ column }) => (
|
header: ({ column }) => (
|
||||||
<Button variant="ghost" onClick={() => column.toggleSorting()} className="w-full justify-start">
|
<Button variant="ghost" onClick={() => column.toggleSorting()} className="w-full justify-start">
|
||||||
Date d'acquisition <ArrowUpDown className="ml-2 h-4 w-4" />
|
Date d'acquisition <ArrowUpDown className="ml-2 h-4 w-4" />
|
||||||
@ -56,22 +86,20 @@ const columns: ColumnDef<Purchase>[] = [
|
|||||||
),
|
),
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const product = row.original.products[0]?.product
|
const product = row.original.products[0]?.product
|
||||||
return (
|
return product?.dateAcquisition ? (
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm text-gray-500">
|
||||||
{new Date(product?.dateAcquisition).toLocaleDateString('fr-FR')}
|
{new Date(product.dateAcquisition).toLocaleDateString('fr-FR')}
|
||||||
</div>
|
</div>
|
||||||
)
|
) : null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "contact",
|
id: "contact",
|
||||||
accessorFn: (row) => `${row.customer.contactFirstName} ${row.customer.contactLastName}`,
|
accessorFn: (row) => `${row.customer.contactFirstName || ''} ${row.customer.contactLastName || ''}`,
|
||||||
header: ({ column }) => (
|
header: () => (
|
||||||
<div className="text-left">Contact</div>
|
<div className="text-left">Contact</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
accessorKey: "amountTotal",
|
accessorKey: "amountTotal",
|
||||||
header: ({ column }) => (
|
header: ({ column }) => (
|
||||||
@ -104,12 +132,12 @@ const columns: ColumnDef<Purchase>[] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export default function Purchases() {
|
export default function Purchases() {
|
||||||
const { data: purchases = [], isLoading: purchasesLoading } = useQuery({
|
const { data: purchases = [], isLoading: purchasesLoading } = useQuery<Purchase[]>({
|
||||||
queryKey: ['purchases'],
|
queryKey: ['purchases'],
|
||||||
queryFn: purchaseService.getAll
|
queryFn: purchaseService.getAll
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: clients = [], isLoading: clientsLoading } = useQuery({
|
const { data: clients = [], isLoading: clientsLoading } = useQuery<Client[]>({
|
||||||
queryKey: ['clients'],
|
queryKey: ['clients'],
|
||||||
queryFn: clientService.getAll
|
queryFn: clientService.getAll
|
||||||
})
|
})
|
||||||
@ -123,7 +151,7 @@ export default function Purchases() {
|
|||||||
if (purchasesLoading || clientsLoading) return <div>Chargement...</div>
|
if (purchasesLoading || clientsLoading) return <div>Chargement...</div>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto py-10">
|
<div className="container mx-auto p4 py-10 p4">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<Select value={selectedClient} onValueChange={setSelectedClient}>
|
<Select value={selectedClient} onValueChange={setSelectedClient}>
|
||||||
<SelectTrigger className="w-[280px]">
|
<SelectTrigger className="w-[280px]">
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export default function Login() {
|
|||||||
<Input
|
<Input
|
||||||
id="email"
|
id="email"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="name@example.com"
|
placeholder="Entrez votre email"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFormData({ ...formData, email: e.target.value })
|
setFormData({ ...formData, email: e.target.value })
|
||||||
@ -54,6 +54,7 @@ export default function Login() {
|
|||||||
<Input
|
<Input
|
||||||
id="password"
|
id="password"
|
||||||
type="password"
|
type="password"
|
||||||
|
placeholder="********"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFormData({ ...formData, password: e.target.value })
|
setFormData({ ...formData, password: e.target.value })
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"lib": [
|
"lib": [
|
||||||
@ -27,7 +26,6 @@
|
|||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noUncheckedSideEffectImports": true
|
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src"
|
"src"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user