chore: update
This commit is contained in:
Vendored
-1510
File diff suppressed because one or more lines are too long
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
<!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-BZ4jKugg.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,92 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
useToast,
|
||||
} from '@chakra-ui/react'
|
||||
import { api, ApiError, type Demo } from '../lib/api'
|
||||
|
||||
interface EditDomainModalProps {
|
||||
demo: Demo | null
|
||||
onClose: () => void
|
||||
onSaved: () => void
|
||||
}
|
||||
|
||||
// Modal d'édition du domaine public d'une démo — partagé entre le dashboard
|
||||
// Demos et le dashboard Premium.
|
||||
export function EditDomainModal({ demo, onClose, onSaved }: EditDomainModalProps) {
|
||||
const toast = useToast()
|
||||
const [domain, setDomain] = useState('')
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setDomain(demo?.custom_domain ?? '')
|
||||
}, [demo])
|
||||
|
||||
const handleClose = () => {
|
||||
if (saving) return
|
||||
onClose()
|
||||
}
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!demo) return
|
||||
setSaving(true)
|
||||
try {
|
||||
await api.setDemoDomain(demo.id, domain.trim())
|
||||
toast({ status: 'success', title: 'Domaine mis à jour' })
|
||||
onSaved()
|
||||
onClose()
|
||||
} catch (err) {
|
||||
const msg = err instanceof ApiError ? err.message : 'Erreur'
|
||||
toast({ status: 'error', title: 'Mise à jour impossible', description: msg })
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={!!demo} onClose={handleClose} closeOnOverlayClick={!saving}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Domaine de la plateforme</ModalHeader>
|
||||
<ModalCloseButton isDisabled={saving} />
|
||||
<ModalBody>
|
||||
<FormControl>
|
||||
<FormLabel fontSize="sm">Domaine personnalisé</FormLabel>
|
||||
<Input
|
||||
placeholder="boutique.mon-domaine.com"
|
||||
value={domain}
|
||||
onChange={(e) => setDomain(e.target.value)}
|
||||
isDisabled={saving}
|
||||
fontFamily="mono"
|
||||
/>
|
||||
<FormHelperText>
|
||||
Laissez vide pour revenir au domaine par défaut ({demo?.namespace}.<domaine
|
||||
omnex>). Le DNS du domaine choisi doit pointer vers le cluster, et un certificat
|
||||
TLS valide doit le couvrir — le certificat wildcard partagé ne couvre que le domaine
|
||||
par défaut.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant="ghost" mr={3} onClick={handleClose} isDisabled={saving}>
|
||||
Annuler
|
||||
</Button>
|
||||
<Button colorScheme="primary" onClick={handleSave} isLoading={saving}>
|
||||
Enregistrer
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
@@ -58,6 +58,7 @@ export interface Demo {
|
||||
status: DemoStatus
|
||||
namespace: string
|
||||
url: string
|
||||
custom_domain?: string
|
||||
type_abonnement?: string
|
||||
created_at: string
|
||||
expires_at: string
|
||||
@@ -186,6 +187,8 @@ export const api = {
|
||||
}),
|
||||
extendDemo: (id: string) => request<Demo>('POST', `/demos/${id}/extend`),
|
||||
deleteDemo: (id: string) => request<Demo>('DELETE', `/demos/${id}`),
|
||||
setDemoDomain: (id: string, domain: string) =>
|
||||
request<Demo>('POST', `/demos/${id}/domain`, { domain }),
|
||||
|
||||
listCodes: () => request<{ items: CodeBuySub[] }>('GET', '/codes'),
|
||||
createCode: (username: string) =>
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Heading,
|
||||
HStack,
|
||||
Icon,
|
||||
IconButton,
|
||||
Link,
|
||||
Spacer,
|
||||
Spinner,
|
||||
@@ -21,11 +22,13 @@ import {
|
||||
Tr,
|
||||
useToast,
|
||||
} from '@chakra-ui/react'
|
||||
import { EditIcon } from '@chakra-ui/icons'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { api, ApiError, type Demo, type DemoDetails } from '../../lib/api'
|
||||
import { statusColor, statusLabel, timeRemaining } from '../../lib/format'
|
||||
import { ConfirmDialog } from '../../components/ConfirmDialog'
|
||||
import { CreateDemoModal } from '../../components/CreateDemoModal'
|
||||
import { EditDomainModal } from '../../components/EditDomainModal'
|
||||
import { ChevronIcon, PodStatusPanel } from '../../components/PodStatusPanel'
|
||||
|
||||
// Un provisioning en cours => on rafraîchit régulièrement.
|
||||
@@ -41,6 +44,7 @@ export function Demos() {
|
||||
const [busy, setBusy] = useState<string | null>(null)
|
||||
const [toDelete, setToDelete] = useState<Demo | null>(null)
|
||||
const [createModalOpen, setCreateModalOpen] = useState(false)
|
||||
const [editingDomain, setEditingDomain] = useState<Demo | null>(null)
|
||||
|
||||
// --- Ligne dépliée (état live des pods) ---
|
||||
const [expandedId, setExpandedId] = useState<string | null>(null)
|
||||
@@ -209,18 +213,30 @@ export function Demos() {
|
||||
<Badge colorScheme={statusColor(d.status)}>{statusLabel(d.status)}</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
{d.status === 'ready' ? (
|
||||
<Link
|
||||
href={d.url}
|
||||
color="primary.500"
|
||||
isExternal
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{d.url}
|
||||
</Link>
|
||||
) : (
|
||||
<Text color="gray.400">—</Text>
|
||||
)}
|
||||
<HStack spacing={1}>
|
||||
{d.status === 'ready' ? (
|
||||
<Link
|
||||
href={d.url}
|
||||
color="primary.500"
|
||||
isExternal
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{d.url}
|
||||
</Link>
|
||||
) : (
|
||||
<Text color="gray.400">—</Text>
|
||||
)}
|
||||
<IconButton
|
||||
aria-label="Modifier le domaine"
|
||||
icon={<EditIcon />}
|
||||
size="xs"
|
||||
variant="ghost"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setEditingDomain(d)
|
||||
}}
|
||||
/>
|
||||
</HStack>
|
||||
</Td>
|
||||
<Td>{isAlive(d.status) ? timeRemaining(d.expires_at) : '—'}</Td>
|
||||
<Td textAlign="right">
|
||||
@@ -293,6 +309,12 @@ export function Demos() {
|
||||
et toutes ses données seront supprimées définitivement. Les ressources du pool seront
|
||||
libérées. Cette action est irréversible.
|
||||
</ConfirmDialog>
|
||||
|
||||
<EditDomainModal
|
||||
demo={editingDomain}
|
||||
onClose={() => setEditingDomain(null)}
|
||||
onSaved={() => void load()}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Heading,
|
||||
HStack,
|
||||
Icon,
|
||||
IconButton,
|
||||
Link,
|
||||
Spacer,
|
||||
Spinner,
|
||||
@@ -21,11 +22,13 @@ import {
|
||||
Tr,
|
||||
useToast,
|
||||
} from '@chakra-ui/react'
|
||||
import { EditIcon } from '@chakra-ui/icons'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { api, ApiError, type Demo, type DemoDetails } from '../../lib/api'
|
||||
import { statusColor, statusLabel } from '../../lib/format'
|
||||
import { ChevronIcon, PodStatusPanel } from '../../components/PodStatusPanel'
|
||||
import { CreateDemoModal } from '../../components/CreateDemoModal'
|
||||
import { EditDomainModal } from '../../components/EditDomainModal'
|
||||
|
||||
// Un provisioning en cours => on rafraîchit régulièrement.
|
||||
const POLL_MS = 5000
|
||||
@@ -38,6 +41,7 @@ export function PremiumDemos() {
|
||||
const [demos, setDemos] = useState<Demo[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [createModalOpen, setCreateModalOpen] = useState(false)
|
||||
const [editingDomain, setEditingDomain] = useState<Demo | null>(null)
|
||||
|
||||
// --- Ligne dépliée (état live des pods) ---
|
||||
const [expandedId, setExpandedId] = useState<string | null>(null)
|
||||
@@ -165,18 +169,30 @@ export function PremiumDemos() {
|
||||
<Badge colorScheme={statusColor(d.status)}>{statusLabel(d.status)}</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
{d.status === 'ready' ? (
|
||||
<Link
|
||||
href={d.url}
|
||||
color="primary.500"
|
||||
isExternal
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{d.url}
|
||||
</Link>
|
||||
) : (
|
||||
<Text color="gray.400">—</Text>
|
||||
)}
|
||||
<HStack spacing={1}>
|
||||
{d.status === 'ready' ? (
|
||||
<Link
|
||||
href={d.url}
|
||||
color="primary.500"
|
||||
isExternal
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{d.url}
|
||||
</Link>
|
||||
) : (
|
||||
<Text color="gray.400">—</Text>
|
||||
)}
|
||||
<IconButton
|
||||
aria-label="Modifier le domaine"
|
||||
icon={<EditIcon />}
|
||||
size="xs"
|
||||
variant="ghost"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setEditingDomain(d)
|
||||
}}
|
||||
/>
|
||||
</HStack>
|
||||
</Td>
|
||||
</Tr>
|
||||
<Tr>
|
||||
@@ -203,6 +219,12 @@ export function PremiumDemos() {
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)}
|
||||
|
||||
<EditDomainModal
|
||||
demo={editingDomain}
|
||||
onClose={() => setEditingDomain(null)}
|
||||
onSaved={() => void load()}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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/Footer.tsx","./src/components/Header.tsx","./src/components/PasswordInput.tsx","./src/components/PodStatusPanel.tsx","./src/components/PublicLayout.tsx","./src/lib/api.ts","./src/lib/auth.tsx","./src/lib/format.test.ts","./src/lib/format.ts","./src/pages/AdminLogin.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.test.tsx","./src/components/ConfirmDialog.tsx","./src/components/CreateDemoModal.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/lib/api.ts","./src/lib/auth.tsx","./src/lib/format.test.ts","./src/lib/format.ts","./src/pages/AdminLogin.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"}
|
||||
Reference in New Issue
Block a user