style(shop): resolve merge conflict in license lookup placeholder
All checks were successful
Staging Build / build (push) Successful in 2m59s
All checks were successful
Staging Build / build (push) Successful in 2m59s
This commit is contained in:
@@ -18,9 +18,18 @@ import {
|
|||||||
Zap,
|
Zap,
|
||||||
ArrowUpRight,
|
ArrowUpRight,
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
|
Calendar,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
Activity
|
Activity
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
} from '@/components/ui/dialog'
|
||||||
|
|
||||||
interface HomeClientProps {
|
interface HomeClientProps {
|
||||||
initialUser: User | null
|
initialUser: User | null
|
||||||
@@ -29,6 +38,7 @@ interface HomeClientProps {
|
|||||||
export function HomeClient({ initialUser }: HomeClientProps) {
|
export function HomeClient({ initialUser }: HomeClientProps) {
|
||||||
const [user, setUser] = useState<User | null>(initialUser)
|
const [user, setUser] = useState<User | null>(initialUser)
|
||||||
const [mounted, setMounted] = useState(false)
|
const [mounted, setMounted] = useState(false)
|
||||||
|
const [calendarOpen, setCalendarOpen] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true)
|
setMounted(true)
|
||||||
@@ -125,23 +135,23 @@ export function HomeClient({ initialUser }: HomeClientProps) {
|
|||||||
Mein Dashboard Schnellzugriff
|
Mein Dashboard Schnellzugriff
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<Link href="/my-customers" className="group">
|
<Link href="/my-customers" className="group">
|
||||||
<motion.div
|
<motion.div
|
||||||
whileHover={{ y: -2 }}
|
whileHover={{ y: -2 }}
|
||||||
whileTap={{ scale: 0.98 }}
|
whileTap={{ scale: 0.98 }}
|
||||||
className="flex items-center justify-between p-4 rounded-xl border border-slate-800 bg-slate-950 hover:bg-slate-900 hover:border-slate-700 transition-all duration-200"
|
className="flex items-center justify-between p-4 rounded-xl border border-slate-800 bg-slate-950 hover:bg-slate-900 hover:border-slate-700 transition-all duration-200 h-full"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-3">
|
||||||
<div className="p-2.5 rounded-lg bg-blue-500/10 text-blue-400 group-hover:bg-blue-500/20 group-hover:text-blue-300 transition-colors">
|
<div className="p-2.5 rounded-lg bg-blue-500/10 text-blue-400 group-hover:bg-blue-500/20 group-hover:text-blue-300 transition-colors shrink-0">
|
||||||
<Users className="w-5.5 h-5.5" />
|
<Users className="w-5 h-5" />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
<span className="block font-bold text-sm text-slate-200 group-hover:text-white transition-colors">Kundenliste</span>
|
<span className="block font-bold text-sm text-slate-200 group-hover:text-white transition-colors">Kundenliste</span>
|
||||||
<span className="block text-xs text-slate-500 group-hover:text-slate-400 transition-colors">Kunden & Lizenzen verwalten</span>
|
<span className="block text-xs text-slate-500 group-hover:text-slate-400 transition-colors">Verwalten</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ArrowUpRight className="w-4.5 h-4.5 text-slate-600 group-hover:text-blue-400 group-hover:translate-x-0.5 group-hover:-translate-y-0.5 transition-all" />
|
<ArrowUpRight className="w-4 h-4 text-slate-600 group-hover:text-blue-400 group-hover:translate-x-0.5 group-hover:-translate-y-0.5 transition-all shrink-0" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
@@ -149,20 +159,42 @@ export function HomeClient({ initialUser }: HomeClientProps) {
|
|||||||
<motion.div
|
<motion.div
|
||||||
whileHover={{ y: -2 }}
|
whileHover={{ y: -2 }}
|
||||||
whileTap={{ scale: 0.98 }}
|
whileTap={{ scale: 0.98 }}
|
||||||
className="flex items-center justify-between p-4 rounded-xl border border-slate-800 bg-slate-950 hover:bg-slate-900 hover:border-slate-700 transition-all duration-200"
|
className="flex items-center justify-between p-4 rounded-xl border border-slate-800 bg-slate-950 hover:bg-slate-900 hover:border-slate-700 transition-all duration-200 h-full"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-3">
|
||||||
<div className="p-2.5 rounded-lg bg-blue-500/10 text-blue-400 group-hover:bg-blue-500/20 group-hover:text-blue-300 transition-colors">
|
<div className="p-2.5 rounded-lg bg-blue-500/10 text-blue-400 group-hover:bg-blue-500/20 group-hover:text-blue-300 transition-colors shrink-0">
|
||||||
<ClipboardList className="w-5.5 h-5.5" />
|
<ClipboardList className="w-5 h-5" />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
<span className="block font-bold text-sm text-slate-200 group-hover:text-white transition-colors">Bestellungen</span>
|
<span className="block font-bold text-sm text-slate-200 group-hover:text-white transition-colors">Bestellungen</span>
|
||||||
<span className="block text-xs text-slate-500 group-hover:text-slate-400 transition-colors">Aufträge & Status einsehen</span>
|
<span className="block text-xs text-slate-500 group-hover:text-slate-400 transition-colors">Status einsehen</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ArrowUpRight className="w-4.5 h-4.5 text-slate-600 group-hover:text-blue-400 group-hover:translate-x-0.5 group-hover:-translate-y-0.5 transition-all" />
|
<ArrowUpRight className="w-4 h-4 text-slate-600 group-hover:text-blue-400 group-hover:translate-x-0.5 group-hover:-translate-y-0.5 transition-all shrink-0" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
<button type="button" onClick={() => setCalendarOpen(true)} className="group text-left w-full">
|
||||||
|
<motion.div
|
||||||
|
whileHover={{ y: -2 }}
|
||||||
|
whileTap={{ scale: 0.98 }}
|
||||||
|
className="flex items-center justify-between p-4 rounded-xl border border-slate-800 bg-slate-950 hover:bg-slate-900 hover:border-slate-700 transition-all duration-200 h-full"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="p-2.5 rounded-lg bg-blue-500/10 text-blue-400 group-hover:bg-blue-500/20 group-hover:text-blue-300 transition-colors shrink-0">
|
||||||
|
<Calendar className="w-5 h-5" />
|
||||||
|
</div>
|
||||||
|
<div className="text-left">
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<span className="block font-bold text-sm text-slate-200 group-hover:text-white transition-colors">Termine</span>
|
||||||
|
<span className="px-1.5 py-0.5 text-[10px] font-medium bg-amber-500/10 text-amber-400 border border-amber-500/20 rounded-md">Geplant</span>
|
||||||
|
</div>
|
||||||
|
<span className="block text-xs text-slate-500 group-hover:text-slate-400 transition-colors">Terminkalender</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ArrowUpRight className="w-4 h-4 text-slate-600 group-hover:text-blue-400 group-hover:translate-x-0.5 group-hover:-translate-y-0.5 transition-all shrink-0" />
|
||||||
|
</motion.div>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
@@ -171,6 +203,52 @@ export function HomeClient({ initialUser }: HomeClientProps) {
|
|||||||
</motion.div>
|
</motion.div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{/* Dialog / Popup für Termin-Kalender (Geplant) */}
|
||||||
|
<Dialog open={calendarOpen} onOpenChange={setCalendarOpen}>
|
||||||
|
<DialogContent className="bg-slate-900 border-slate-800 text-slate-100 max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<div className="p-2.5 rounded-xl bg-blue-500/10 text-blue-400 border border-blue-500/20">
|
||||||
|
<Calendar className="w-6 h-6" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<DialogTitle className="text-xl font-bold text-white flex items-center gap-2">
|
||||||
|
Termin-Kalender
|
||||||
|
<span className="px-2 py-0.5 text-xs font-semibold bg-amber-500/10 text-amber-400 border border-amber-500/20 rounded-full">
|
||||||
|
In Planung
|
||||||
|
</span>
|
||||||
|
</DialogTitle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DialogDescription className="text-slate-400 text-sm leading-relaxed pt-2">
|
||||||
|
Der Termin-Kalender bietet in Kürze eine Übersicht für anstehende Schulungen, Online- & Vor-Ort-Workshops sowie Termine für Blauer-Mittwoch-Partner zur direkten Anmeldung.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="p-4 my-2 rounded-xl bg-slate-950 border border-slate-800 text-xs text-slate-400 space-y-2">
|
||||||
|
<div className="flex items-center justify-between text-slate-300 font-medium">
|
||||||
|
<span>Geplante Features & Angebote:</span>
|
||||||
|
</div>
|
||||||
|
<ul className="list-disc list-inside space-y-1 text-slate-400">
|
||||||
|
<li>Anmeldung zu Online- & Vor-Ort-Workshops</li>
|
||||||
|
<li>Anstehende Schulungstermine auf einen Blick</li>
|
||||||
|
<li>Spezielle Schulungen für Blauer-Mittwoch-Partner</li>
|
||||||
|
<li>Kalender-Export (.ics / Google / Outlook)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => setCalendarOpen(false)}
|
||||||
|
className="w-full sm:w-auto border-slate-700 bg-slate-800 text-slate-200 hover:bg-slate-700 hover:text-white"
|
||||||
|
>
|
||||||
|
Schließen
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
{/* Grid of modules */}
|
{/* Grid of modules */}
|
||||||
<section className="w-full py-16 md:py-24 border-t border-slate-900 bg-slate-950/50 relative">
|
<section className="w-full py-16 md:py-24 border-t border-slate-900 bg-slate-950/50 relative">
|
||||||
<div className="container max-w-6xl mx-auto px-4 relative">
|
<div className="container max-w-6xl mx-auto px-4 relative">
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ export function OrderWizard({
|
|||||||
return 'one_time'
|
return 'one_time'
|
||||||
})
|
})
|
||||||
|
|
||||||
// Falls "one_time" (Kauf) gewählt: optionales Datum der letzten CAS-Lizenzierung
|
// Falls "one_time" (Kauf) gewählt: optionales Datum der letzten CASPOS-Lizenzierung
|
||||||
const [lastLicenseDate, setLastLicenseDate] = useState<string>(
|
const [lastLicenseDate, setLastLicenseDate] = useState<string>(
|
||||||
initialOrder?.order_data?.last_license_date || ''
|
initialOrder?.order_data?.last_license_date || ''
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,12 +25,12 @@ import { lookupLicenseFromLicServer } from '@/lib/actions/licserver-config'
|
|||||||
|
|
||||||
// ─── MOCK DATA (until real LicServer API is wired up) ───────────────────────
|
// ─── MOCK DATA (until real LicServer API is wired up) ───────────────────────
|
||||||
const MOCK_LICENSES: Record<string, LicenseInfo> = {
|
const MOCK_LICENSES: Record<string, LicenseInfo> = {
|
||||||
'995500-0005': {
|
'995502-00': {
|
||||||
licenseKey: '995500-0005',
|
licenseKey: '995502-00',
|
||||||
status: 'active',
|
status: 'active',
|
||||||
product: 'CASPOS DEMO',
|
product: 'CASPOS',
|
||||||
edition: 'Professional',
|
edition: 'GASTRO',
|
||||||
version: '14.1.2',
|
version: '4.8.6',
|
||||||
seats: 10,
|
seats: 10,
|
||||||
customer: 'Mustermann GmbH',
|
customer: 'Mustermann GmbH',
|
||||||
contact: 'Max Mustermann',
|
contact: 'Max Mustermann',
|
||||||
@@ -39,33 +39,33 @@ const MOCK_LICENSES: Record<string, LicenseInfo> = {
|
|||||||
maintenanceUntil: '2025-03-31',
|
maintenanceUntil: '2025-03-31',
|
||||||
modules: ['CRM', 'ERP-Link', 'Mobile Client', 'Web Client'],
|
modules: ['CRM', 'ERP-Link', 'Mobile Client', 'Web Client'],
|
||||||
},
|
},
|
||||||
'CAS-2020-STD-00456': {
|
'995501-00': {
|
||||||
licenseKey: 'CAS-2020-STD-00456',
|
licenseKey: '995501-00',
|
||||||
status: 'expired',
|
status: 'expired',
|
||||||
product: 'CAS genesisWorld Standard',
|
product: 'CASPOS Handel',
|
||||||
edition: 'Standard',
|
edition: 'Standard',
|
||||||
version: '12.0.0',
|
version: '3.12.1',
|
||||||
seats: 5,
|
seats: 1,
|
||||||
customer: 'Beispiel AG',
|
customer: 'Beispiel AG',
|
||||||
contact: 'Erika Muster',
|
contact: 'Erika Muster',
|
||||||
issuedAt: '2020-01-15',
|
issuedAt: '2016-03-14',
|
||||||
expiresAt: '2023-01-14',
|
expiresAt: '2017-03-14',
|
||||||
maintenanceUntil: '2022-01-14',
|
maintenanceUntil: '2017-03-14',
|
||||||
modules: ['CRM'],
|
modules: ['CASPOS Handel'],
|
||||||
},
|
},
|
||||||
'CAS-2024-ENT-00789': {
|
'995500-00': {
|
||||||
licenseKey: 'CAS-2024-ENT-00789',
|
licenseKey: '995500-00',
|
||||||
status: 'active',
|
status: 'active',
|
||||||
product: 'CAS genesisWorld Enterprise',
|
product: 'CASPOS Handel',
|
||||||
edition: 'Enterprise',
|
edition: 'Standard',
|
||||||
version: '15.0.0',
|
version: '4.11.2',
|
||||||
seats: 50,
|
seats: 2,
|
||||||
customer: 'Tech Solutions GmbH & Co. KG',
|
customer: 'Tech Solutions GmbH & Co. KG',
|
||||||
contact: 'Julia Schneider',
|
contact: 'Julia Schneider',
|
||||||
issuedAt: '2024-01-01',
|
issuedAt: '2026-01-01',
|
||||||
expiresAt: '2026-12-31',
|
expiresAt: '2027-12-31',
|
||||||
maintenanceUntil: '2026-12-31',
|
maintenanceUntil: '2027-12-31',
|
||||||
modules: ['CRM', 'ERP-Link', 'Mobile Client', 'Web Client', 'AI Assistant', 'Analytics Pro'],
|
modules: ['CASPOS Handel'],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +224,7 @@ export function LicenseLookupPanel({ onLicenseResolved }: LicenseLookupPanelProp
|
|||||||
{/* ── Input + Search Button ──────────────────────────────── */}
|
{/* ── Input + Search Button ──────────────────────────────── */}
|
||||||
<div className="pt-4">
|
<div className="pt-4">
|
||||||
<p className="text-xs text-slate-400 mb-2 leading-relaxed">
|
<p className="text-xs text-slate-400 mb-2 leading-relaxed">
|
||||||
Geben Sie Ihre bisherige CAS-Lizenznummer ein,
|
Geben Sie Ihre bisherige CASPOS-Lizenznummer ein,
|
||||||
um Lizenzinformationen automatisch abzurufen.
|
um Lizenzinformationen automatisch abzurufen.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -278,16 +278,16 @@ export function LicenseLookupPanel({ onLicenseResolved }: LicenseLookupPanelProp
|
|||||||
Demo:
|
Demo:
|
||||||
<span
|
<span
|
||||||
className="font-mono text-violet-400/80 cursor-pointer hover:text-violet-400 transition-colors"
|
className="font-mono text-violet-400/80 cursor-pointer hover:text-violet-400 transition-colors"
|
||||||
onClick={() => setLicenseKey('995500-00')}
|
onClick={() => setLicenseKey('995502-00')}
|
||||||
>
|
>
|
||||||
995500-00
|
995502-00
|
||||||
</span>
|
</span>
|
||||||
{' · '}
|
{' · '}
|
||||||
<span
|
<span
|
||||||
className="font-mono text-amber-400/80 cursor-pointer hover:text-amber-400 transition-colors"
|
className="font-mono text-amber-400/80 cursor-pointer hover:text-amber-400 transition-colors"
|
||||||
onClick={() => setLicenseKey('995500-01')}
|
onClick={() => setLicenseKey('995501-00')}
|
||||||
>
|
>
|
||||||
995500-01
|
995501-00 (abgelaufen)
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -44,8 +44,7 @@ export function StepBilling({
|
|||||||
{/* Option 1: Kauf */}
|
{/* Option 1: Kauf */}
|
||||||
<div
|
<div
|
||||||
onClick={() => handleBillingIntervalChange('one_time')}
|
onClick={() => handleBillingIntervalChange('one_time')}
|
||||||
className={`relative p-6 rounded-2xl border-2 cursor-pointer transition-all duration-300 flex flex-col justify-between h-48 hover:shadow-[0_0_20px_rgba(255,255,255,0.05)] ${
|
className={`relative p-6 rounded-2xl border-2 cursor-pointer transition-all duration-300 flex flex-col justify-between h-48 hover:shadow-[0_0_20px_rgba(255,255,255,0.05)] ${selectedBillingInterval === 'one_time'
|
||||||
selectedBillingInterval === 'one_time'
|
|
||||||
? 'border-primary bg-primary/10 shadow-[0_0_25px_rgba(59,130,246,0.2)]'
|
? 'border-primary bg-primary/10 shadow-[0_0_25px_rgba(59,130,246,0.2)]'
|
||||||
: 'border-white/5 bg-white/5 hover:border-white/20'
|
: 'border-white/5 bg-white/5 hover:border-white/20'
|
||||||
}`}
|
}`}
|
||||||
@@ -71,8 +70,7 @@ export function StepBilling({
|
|||||||
{/* Option 2: Abo */}
|
{/* Option 2: Abo */}
|
||||||
<div
|
<div
|
||||||
onClick={() => handleBillingIntervalChange('monthly')}
|
onClick={() => handleBillingIntervalChange('monthly')}
|
||||||
className={`relative p-6 rounded-2xl border-2 cursor-pointer transition-all duration-300 flex flex-col justify-between h-48 hover:shadow-[0_0_20px_rgba(255,255,255,0.05)] ${
|
className={`relative p-6 rounded-2xl border-2 cursor-pointer transition-all duration-300 flex flex-col justify-between h-48 hover:shadow-[0_0_20px_rgba(255,255,255,0.05)] ${selectedBillingInterval === 'monthly'
|
||||||
selectedBillingInterval === 'monthly'
|
|
||||||
? 'border-primary bg-primary/10 shadow-[0_0_25px_rgba(59,130,246,0.2)]'
|
? 'border-primary bg-primary/10 shadow-[0_0_25px_rgba(59,130,246,0.2)]'
|
||||||
: 'border-white/5 bg-white/5 hover:border-white/20'
|
: 'border-white/5 bg-white/5 hover:border-white/20'
|
||||||
}`}
|
}`}
|
||||||
@@ -99,7 +97,7 @@ export function StepBilling({
|
|||||||
<div className="p-4 rounded-xl bg-white/5 border border-white/10 space-y-3 mt-6 max-w-md animate-in fade-in slide-in-from-top-2 duration-300">
|
<div className="p-4 rounded-xl bg-white/5 border border-white/10 space-y-3 mt-6 max-w-md animate-in fade-in slide-in-from-top-2 duration-300">
|
||||||
<Label htmlFor="last-license-date" className="text-white font-medium flex items-center gap-2">
|
<Label htmlFor="last-license-date" className="text-white font-medium flex items-center gap-2">
|
||||||
<Calendar className="w-4 h-4 text-primary" />
|
<Calendar className="w-4 h-4 text-primary" />
|
||||||
Datum der letzten CAS-Lizenz (falls vorhanden)
|
Datum der letzten CASPOS-Lizenz (falls vorhanden)
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
id="last-license-date"
|
id="last-license-date"
|
||||||
|
|||||||
Reference in New Issue
Block a user