feat: reseller self-service portal (clients + licenses + tickets)
- ResellerPortalView with 3 tabs:
1. Kunden: list agency's clients + create new client (POST /api/aos/clients)
2. Lizenzen: list agency-filtered licenses + create new license with
live pricing preview (base + agency discount + yearly discount = net)
3. Tickets: agency-scoped ticket inbox + escalate button
- Top: agency selector (pick which approved reseller to act as)
- Full integration: store, sidebar, top-bar, command-menu, i18n, app-shell
- Mobile responsive, German labels, no indigo/blue
This commit is contained in:
1 parent
ec5e6b1629
commit
39b3636333
9 files changed
+3071
No files matched your search
@@ -19,6 +19,7 @@ import { TimesheetView } from './views/timesheet'
|
||||
import { SupportView } from './views/support'
|
||||
import { LicensesView } from './views/licenses'
|
||||
import { AgencyView } from './views/agency'
|
||||
import { ResellerPortalView } from './views/reseller-portal'
|
||||
import { InstancesView } from './views/instances'
|
||||
import { ContractsView } from './views/contracts'
|
||||
import { ExpensesView } from './views/expenses'
|
||||
@@ -97,6 +98,7 @@ export function AppShell() {
|
||||
{view === 'support' && <SupportView />}
|
||||
{view === 'licenses' && <LicensesView />}
|
||||
{view === 'agency' && <AgencyView />}
|
||||
{view === 'reseller_portal' && <ResellerPortalView />}
|
||||
{view === 'instances' && <InstancesView />}
|
||||
{view === 'contracts' && <ContractsView />}
|
||||
{view === 'expenses' && <ExpensesView />}
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
CalendarDays,
|
||||
Database,
|
||||
Server,
|
||||
Store,
|
||||
} from 'lucide-react'
|
||||
import { useAos, type ViewId } from '@/store/aos'
|
||||
import {
|
||||
@@ -51,6 +52,7 @@ const VIEWS: { id: ViewId; label: string; icon: typeof LayoutDashboard; hint: st
|
||||
{ id: 'support', label: 'Support', icon: LifeBuoy, hint: 'Tickets & SLA' },
|
||||
{ id: 'licenses', label: 'Lizenzen', icon: KeyRound, hint: 'Ausgestellt & Erneuerungen' },
|
||||
{ id: 'agency', label: 'Reseller', icon: Users, hint: 'Reseller-Bewerbungen & Rabatte' },
|
||||
{ id: 'reseller_portal', label: 'Reseller-Portal', icon: Store, hint: 'Selbstbedienung: Kunden, Lizenzen, Tickets' },
|
||||
{ id: 'instances', label: 'Instanzen', icon: Server, hint: 'Tenant-Instanzen & Provisioning' },
|
||||
{ id: 'contracts', label: 'Verträge', icon: FileSignature, hint: 'Werkvertrag, AVV, NDA' },
|
||||
{ id: 'expenses', label: 'Ausgaben', icon: Wallet, hint: 'Belastbar & DATEV' },
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
RefreshCw,
|
||||
Landmark,
|
||||
Server,
|
||||
Store,
|
||||
} from 'lucide-react'
|
||||
import { useAos, type ViewId } from '@/store/aos'
|
||||
import { translate } from '@/lib/i18n'
|
||||
@@ -69,6 +70,7 @@ const NAV: {
|
||||
items: [
|
||||
{ id: 'licenses', label: 'Lizenzen', icon: KeyRound, permission: 'licenses:read' },
|
||||
{ id: 'agency', label: 'Reseller', icon: Users, permission: 'admin' },
|
||||
{ id: 'reseller_portal', label: 'Reseller-Portal', icon: Store, permission: 'admin' },
|
||||
{ id: 'instances', label: 'Instanzen', icon: Server, permission: 'admin' },
|
||||
{ id: 'contracts', label: 'Verträge', icon: FileSignature, permission: 'contracts:read' },
|
||||
{ id: 'expenses', label: 'Ausgaben', icon: Wallet, permission: 'expenses:read' },
|
||||
|
||||
@@ -29,6 +29,7 @@ const TITLES: Record<ViewId, { title: string; crumb: string }> = {
|
||||
support: { title: 'Support', crumb: 'Lieferung / Tickets' },
|
||||
licenses: { title: 'Lizenzen', crumb: 'Betrieb / Lizenzen' },
|
||||
agency: { title: 'Reseller', crumb: 'Betrieb / Reseller' },
|
||||
reseller_portal: { title: 'Reseller-Portal', crumb: 'Betrieb / Reseller-Portal' },
|
||||
instances: { title: 'Instanzen', crumb: 'Betrieb / Instanzen' },
|
||||
contracts: { title: 'Verträge', crumb: 'Betrieb / Verträge' },
|
||||
expenses: { title: 'Ausgaben', crumb: 'Betrieb / Ausgaben' },
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user