chore: build
ci-web / test (push) Canceled after 0s

This commit is contained in:
Xor290
2026-08-19 12:33:46 +02:00
parent e19fa4e82c
commit 159ac259c9
9 changed files with 1619 additions and 79 deletions
+1510
View File
File diff suppressed because one or more lines are too long
+13
View File
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Omnex — Plateforme de gestion de commandes & livraison</title>
<meta name="description" content="Déployez en un clic une démo complète de la plateforme de gestion de commandes et de livraison." />
<script type="module" crossorigin src="/assets/index-CbXT_Xos.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
+4
View File
@@ -14,8 +14,10 @@ import { AppDownloads } from './pages/backoffice/AppDownloads'
import { Profile } from './pages/backoffice/Profile'
import { PublicLayout } from './components/PublicLayout'
import { BackofficeLayout } from './components/BackofficeLayout'
import { Contact } from './pages/Contact'
import { useAuth } from './lib/auth'
import type { JSX } from 'react'
import { MyDemoPlat } from './pages/backoffice/MyDemoPlat'
// Chemins admin uniquement : un accès non authentifié y renvoie vers la
// page de connexion admin plutôt que la page client.
@@ -61,6 +63,7 @@ export function App() {
<Route path="/" element={<Landing />} />
<Route path="/tarifs" element={<Pricing />} />
<Route path="/documentation" element={<Documentation />} />
<Route path="/contact" element={<Contact />} />
</Route>
<Route path="/login" element={<Login />} />
<Route path="/admin/login" element={<AdminLogin />} />
@@ -106,6 +109,7 @@ export function App() {
<Route path="subscription" element={<Subscription />} />
<Route path="downloads" element={<AppDownloads />} />
<Route path="profile" element={<Profile />} />
<Route path="myservices" element={<MyDemoPlat />} />
</Route>
<Route path="*" element={<Navigate to="/" replace />} />
-27
View File
@@ -1,27 +0,0 @@
import { describe, expect, it } from 'vitest'
import { statusColor, statusLabel, timeRemaining } from './format'
describe('format', () => {
it('mappe les statuts vers un libellé FR', () => {
expect(statusLabel('ready')).toBe('Active')
expect(statusLabel('provisioning')).toBe('Déploiement…')
expect(statusLabel('expired')).toBe('Expirée')
})
it('associe une couleur cohérente au statut', () => {
expect(statusColor('ready')).toBe('green')
expect(statusColor('failed')).toBe('red')
expect(statusColor('expiring')).toBe('orange')
})
it('calcule le temps restant en jours/heures', () => {
const now = Date.parse('2026-07-01T00:00:00Z')
const in30d = '2026-07-31T00:00:00Z'
expect(timeRemaining(in30d, now)).toBe('30 j 0 h')
})
it('renvoie "expirée" quand l’échéance est passée', () => {
const now = Date.parse('2026-07-10T00:00:00Z')
expect(timeRemaining('2026-07-01T00:00:00Z', now)).toBe('expirée')
})
})
+1
View File
@@ -362,6 +362,7 @@ export function Demos() {
isOpen={!!toExtend}
title="Prolonger la démo de 30 jours ?"
confirmLabel="Prolonger"
confirmColorScheme="primary"
isLoading={!!toExtend && busy === toExtend.id}
onConfirm={confirmExtend}
onClose={() => setToExtend(null)}
+87
View File
@@ -0,0 +1,87 @@
import { useCallback, useEffect, useState } from 'react'
import {
Badge,
Box,
Flex,
Heading,
HStack,
Link,
Spacer,
Spinner,
Text,
VStack,
useToast,
} from '@chakra-ui/react'
import { useNavigate } from 'react-router-dom'
import { api, ApiError, type Demo } from '../../lib/api'
import { statusColor, statusLabel } from '../../lib/format'
export function MyDemoPlat() {
const toast = useToast()
const navigate = useNavigate()
const [typeAbo, setTypeAbo] = useState<string | null>(null)
const [demos, setDemos] = useState<Demo[]>([])
const [demosLoading, setDemosLoading] = useState(true)
const load = useCallback(async () => {
try {
const me = await api.me()
setTypeAbo(me.type_abonnement ?? null)
} catch (err) {
if (err instanceof ApiError && err.status === 401) navigate('/login')
}
}, [navigate])
const loadDemos = useCallback(async () => {
try {
const res = await api.listMyDemos()
setDemos(res.items ?? [])
} catch {
toast({ status: 'error', title: 'Chargement des démos impossible' })
} finally {
setDemosLoading(false)
}
}, [toast])
useEffect(() => {
void load()
void loadDemos()
}, [load, loadDemos])
const isPremium = typeAbo === 'premium'
return (
<>
<Flex mb={6} align="center">
<Heading size="md">{isPremium ? 'Ma plateforme' : 'Ma démo'}</Heading>
<Spacer />
</Flex>
<Box mb={8} p={6} borderWidth="1px" borderRadius="lg" bg="bg-surface">
{demosLoading ? (
<Spinner size="sm" />
) : demos.length === 0 ? (
<Text color="gray.500">
{isPremium ? 'Aucune plateforme pour le moment.' : 'Aucune démo pour le moment.'}
</Text>
) : (
<VStack align="stretch" spacing={3}>
{demos.map((d) => (
<HStack key={d.id} justify="space-between" flexWrap="wrap" rowGap={2}>
{d.status === 'ready' ? (
<Link href={d.url} color="primary.500" isExternal fontFamily="mono">
{d.url}
</Link>
) : (
<Text color="gray.400" fontFamily="mono">
{d.url || '—'}
</Text>
)}
<Badge colorScheme={statusColor(d.status)}>{statusLabel(d.status)}</Badge>
</HStack>
))}
</VStack>
)}
</Box>
</>
)
}
+1 -1
View File
@@ -146,7 +146,7 @@ export function PremiumDemos() {
<>
<Flex mb={2} align="center" gap={4} wrap="wrap">
<Heading size="md" mr={4}>
Démos Premium
Plateforme Premium
</Heading>
<Spacer />
<Button colorScheme="primary" onClick={() => setCreateModalOpen(true)}>
+2 -50
View File
@@ -9,7 +9,6 @@ import {
Heading,
HStack,
Input,
Link,
Spacer,
Spinner,
Stack,
@@ -18,8 +17,7 @@ import {
useToast,
} from '@chakra-ui/react'
import { useNavigate } from 'react-router-dom'
import { api, ApiError, type Demo } from '../../lib/api'
import { statusColor, statusLabel } from '../../lib/format'
import { api, ApiError } from '../../lib/api'
export function Subscription() {
const toast = useToast()
@@ -30,8 +28,6 @@ export function Subscription() {
const [busy, setBusy] = useState(false)
const [code, setCode] = useState('')
const [showRenewForm, setShowRenewForm] = useState(false)
const [demos, setDemos] = useState<Demo[]>([])
const [demosLoading, setDemosLoading] = useState(true)
const load = useCallback(async () => {
try {
@@ -46,21 +42,9 @@ export function Subscription() {
}
}, [navigate, toast])
const loadDemos = useCallback(async () => {
try {
const res = await api.listMyDemos()
setDemos(res.items ?? [])
} catch {
// Silencieux : l'absence de démo n'est pas une erreur à afficher ici.
} finally {
setDemosLoading(false)
}
}, [])
useEffect(() => {
void load()
void loadDemos()
}, [load, loadDemos])
}, [load])
const submitCode = async (e: React.FormEvent) => {
e.preventDefault()
@@ -92,38 +76,6 @@ export function Subscription() {
return (
<>
<Flex mb={6} align="center">
<Heading size="md">{isPremium ? 'Ma plateforme' : 'Ma démo'}</Heading>
<Spacer />
</Flex>
<Box mb={8} p={6} borderWidth="1px" borderRadius="lg" bg="bg-surface">
{demosLoading ? (
<Spinner size="sm" />
) : demos.length === 0 ? (
<Text color="gray.500">
{isPremium ? 'Aucune plateforme pour le moment.' : 'Aucune démo pour le moment.'}
</Text>
) : (
<VStack align="stretch" spacing={3}>
{demos.map((d) => (
<HStack key={d.id} justify="space-between" flexWrap="wrap" rowGap={2}>
{d.status === 'ready' ? (
<Link href={d.url} color="primary.500" isExternal fontFamily="mono">
{d.url}
</Link>
) : (
<Text color="gray.400" fontFamily="mono">
{d.url || '—'}
</Text>
)}
<Badge colorScheme={statusColor(d.status)}>{statusLabel(d.status)}</Badge>
</HStack>
))}
</VStack>
)}
</Box>
<Flex mb={6} align="center">
<Heading size="md">Mon abonnement</Heading>
<Spacer />
+1 -1
View File
@@ -1 +1 @@
{"root":["./src/App.tsx","./src/main.tsx","./src/setupTests.ts","./src/theme.ts","./src/vite-env.d.ts","./src/components/BackofficeLayout.tsx","./src/components/ColorModeToggle.tsx","./src/components/ConfirmDialog.test.tsx","./src/components/ConfirmDialog.tsx","./src/components/CreateDemoModal.tsx","./src/components/DemoCard.tsx","./src/components/EditDomainModal.tsx","./src/components/Footer.tsx","./src/components/Header.tsx","./src/components/PasswordInput.tsx","./src/components/PodStatusPanel.tsx","./src/components/PublicLayout.tsx","./src/components/docs/AdminMockup.tsx","./src/components/docs/sections/AdressesSection.tsx","./src/components/docs/sections/AlertesSection.tsx","./src/components/docs/sections/CategoriesSection.tsx","./src/components/docs/sections/CommandesSection.tsx","./src/components/docs/sections/DashboardSection.tsx","./src/components/docs/sections/LivraisonSection.tsx","./src/components/docs/sections/ParametresSection.tsx","./src/components/docs/sections/ProduitsSection.tsx","./src/components/docs/sections/StatistiquesSection.tsx","./src/components/docs/sections/UtilisateursSection.tsx","./src/components/docs/sections/cabine/CabineAlertsSection.tsx","./src/components/docs/sections/cabine/CabineDashboardSection.tsx","./src/components/docs/sections/cabine/CabineDeliverySection.tsx","./src/components/docs/sections/cabine/CabineOrdersSection.tsx","./src/components/docs/sections/cabine/CabineUsersSection.tsx","./src/components/docs/sections/delivery/DeliveryAlertsSection.tsx","./src/components/docs/sections/delivery/DeliveryDashboardSection.tsx","./src/components/docs/sections/delivery/DeliveryRatingsSection.tsx","./src/components/docs/sections/delivery/DeliveryStatsSection.tsx","./src/lib/api.ts","./src/lib/auth.tsx","./src/lib/format.test.ts","./src/lib/format.ts","./src/pages/AdminLogin.tsx","./src/pages/Documentation.tsx","./src/pages/Landing.tsx","./src/pages/Login.tsx","./src/pages/Pricing.tsx","./src/pages/Register.tsx","./src/pages/backoffice/Codes.tsx","./src/pages/backoffice/Demos.tsx","./src/pages/backoffice/PremiumDemos.tsx","./src/pages/backoffice/Profile.tsx","./src/pages/backoffice/Subscription.tsx"],"version":"5.9.3"}
{"root":["./src/App.tsx","./src/main.tsx","./src/setupTests.ts","./src/theme.ts","./src/vite-env.d.ts","./src/components/BackofficeLayout.tsx","./src/components/ColorModeToggle.tsx","./src/components/ConfirmDialog.tsx","./src/components/CreateDemoModal.tsx","./src/components/DemoCard.tsx","./src/components/EditDomainModal.tsx","./src/components/Footer.tsx","./src/components/Header.tsx","./src/components/PasswordInput.tsx","./src/components/PodStatusPanel.tsx","./src/components/PublicLayout.tsx","./src/components/docs/AdminMockup.tsx","./src/components/docs/sections/AdressesSection.tsx","./src/components/docs/sections/AlertesSection.tsx","./src/components/docs/sections/CategoriesSection.tsx","./src/components/docs/sections/CommandesSection.tsx","./src/components/docs/sections/DashboardSection.tsx","./src/components/docs/sections/LivraisonSection.tsx","./src/components/docs/sections/ParametresSection.tsx","./src/components/docs/sections/ProduitsSection.tsx","./src/components/docs/sections/StatistiquesSection.tsx","./src/components/docs/sections/UtilisateursSection.tsx","./src/components/docs/sections/cabine/CabineAlertsSection.tsx","./src/components/docs/sections/cabine/CabineDashboardSection.tsx","./src/components/docs/sections/cabine/CabineDeliverySection.tsx","./src/components/docs/sections/cabine/CabineOrdersSection.tsx","./src/components/docs/sections/cabine/CabineUsersSection.tsx","./src/components/docs/sections/delivery/DeliveryAlertsSection.tsx","./src/components/docs/sections/delivery/DeliveryDashboardSection.tsx","./src/components/docs/sections/delivery/DeliveryRatingsSection.tsx","./src/components/docs/sections/delivery/DeliveryStatsSection.tsx","./src/lib/api.ts","./src/lib/auth.tsx","./src/lib/format.ts","./src/pages/AdminLogin.tsx","./src/pages/Contact.tsx","./src/pages/Documentation.tsx","./src/pages/Landing.tsx","./src/pages/Login.tsx","./src/pages/Pricing.tsx","./src/pages/Register.tsx","./src/pages/backoffice/AppDownloads.tsx","./src/pages/backoffice/Codes.tsx","./src/pages/backoffice/Demos.tsx","./src/pages/backoffice/MyDemoPlat.tsx","./src/pages/backoffice/PremiumDemos.tsx","./src/pages/backoffice/Profile.tsx","./src/pages/backoffice/Subscription.tsx"],"version":"5.9.3"}