diff --git a/src/layouts/MainLayout.module.css b/src/layouts/MainLayout.module.css index 15caf06..faf2439 100644 --- a/src/layouts/MainLayout.module.css +++ b/src/layouts/MainLayout.module.css @@ -108,8 +108,10 @@ min-width: 0; display: flex; flex-direction: column; - overflow-x: hidden; + /* Allow horizontal scroll when inner content has a true minimum width; hiding X clips dashboard/store grids on narrow viewports */ + overflow-x: auto; overflow-y: auto; + -webkit-overflow-scrolling: touch; } .mobileTopBar { diff --git a/src/pages/Dashboard.module.css b/src/pages/Dashboard.module.css index f5dc49a..fa96d0c 100644 --- a/src/pages/Dashboard.module.css +++ b/src/pages/Dashboard.module.css @@ -3,9 +3,12 @@ */ .dashboard { - padding: 2rem; + box-sizing: border-box; + width: 100%; max-width: 1200px; margin: 0 auto; + padding: 2rem; + min-width: 0; } /* Header */ @@ -50,10 +53,10 @@ color: var(--text-primary, #1a1a1a); } -/* Instance Grid */ +/* Instance Grid — min(100%, Npx) keeps one fluid column on narrow viewports (no horizontal clip) */ .instanceGrid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); gap: 1rem; } @@ -63,6 +66,7 @@ align-items: center; gap: 1rem; padding: 1.25rem; + min-width: 0; background: var(--surface-color, #ffffff); border: 1px solid var(--border-color, #e0e0e0); border-radius: 12px; @@ -123,9 +127,8 @@ font-size: 1rem; font-weight: 600; color: var(--text-primary, #1a1a1a); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + overflow-wrap: anywhere; + line-height: 1.3; } .mandateName { @@ -245,3 +248,23 @@ :global(.dark-theme) .emptyState p { color: var(--text-secondary-dark, #aaa); } + +@media (max-width: 600px) { + .dashboard { + padding: 1rem 0.875rem; + } + + .header h1 { + font-size: 1.35rem; + } + + .instanceCard { + padding: 1rem; + gap: 0.75rem; + } + + .cardIcon { + width: 40px; + height: 40px; + } +} diff --git a/src/pages/Store.module.css b/src/pages/Store.module.css index c4a67da..6383188 100644 --- a/src/pages/Store.module.css +++ b/src/pages/Store.module.css @@ -3,9 +3,12 @@ */ .store { - padding: 2rem; + box-sizing: border-box; + width: 100%; max-width: 1000px; margin: 0 auto; + padding: 2rem; + min-width: 0; } /* Header */ @@ -39,6 +42,7 @@ border: 1px solid var(--border-color, #e0e0e0); border-radius: 12px; padding: 1.5rem; + min-width: 0; display: flex; flex-direction: column; gap: 1rem; @@ -59,6 +63,7 @@ display: flex; align-items: center; gap: 0.75rem; + min-width: 0; } .cardIcon { @@ -72,6 +77,8 @@ font-size: 1.125rem; font-weight: 600; color: var(--text-primary, #1a1a1a); + min-width: 0; + overflow-wrap: anywhere; } .cardBody { @@ -260,3 +267,17 @@ :global(.dark-theme) .loading { color: var(--text-secondary-dark, #aaa); } + +@media (max-width: 600px) { + .store { + padding: 1rem 0.875rem; + } + + .header h1 { + font-size: 1.35rem; + } + + .card { + padding: 1.125rem; + } +} diff --git a/src/pages/admin/Admin.module.css b/src/pages/admin/Admin.module.css index 20e14f6..a50aa0f 100644 --- a/src/pages/admin/Admin.module.css +++ b/src/pages/admin/Admin.module.css @@ -905,6 +905,51 @@ background: var(--bg-tertiary, #f8f9fa); } +.accessOverviewSubheading { + font-size: 0.72rem; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--text-secondary, #666); + margin: 1rem 0 0.5rem; +} + +.accessOverviewSubheading:first-child { + margin-top: 0; +} + +.accessOverviewRoleBullets { + margin: 0.25rem 0 0.85rem; + padding-left: 1.25rem; + font-size: 0.875rem; + line-height: 1.55; +} + +.accessOverviewInstanceStack { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.accessOverviewInstanceBlock { + padding: 0.75rem 1rem; + background: var(--bg-secondary, #f5f5f5); + border-radius: 6px; + border: 1px solid var(--border-color, #e0e0e0); +} + +.accessOverviewInstanceTitle { + font-weight: 600; + font-size: 0.9375rem; + color: var(--text-primary, #1a1a1a); + margin-bottom: 0.35rem; +} + +.accessOverviewInstanceFeature { + font-weight: 500; + color: var(--text-secondary, #666); +} + .emptyHint { font-size: 0.875rem; color: var(--text-tertiary, #999); diff --git a/src/pages/admin/AdminUserAccessOverviewPage.tsx b/src/pages/admin/AdminUserAccessOverviewPage.tsx index 72adbc6..fdea742 100644 --- a/src/pages/admin/AdminUserAccessOverviewPage.tsx +++ b/src/pages/admin/AdminUserAccessOverviewPage.tsx @@ -47,7 +47,7 @@ interface AccessEntry { interface MandateInfo { id: string; name: string; - label?: string; + label?: string | null; roleIds: string[]; featureInstances: { id: string; @@ -58,6 +58,18 @@ interface MandateInfo { }[]; } +function _mandateNameLine(mandate: MandateInfo): string { + const label = mandate.label?.trim(); + if (label) { + return `${mandate.name} (${label})`; + } + return mandate.name; +} + +function _roleDescriptionLine(role: RoleInfo): string { + return role.description?.de || role.description?.en || ''; +} + interface UserAccessOverview { user: UserOption; isSysAdmin: boolean; @@ -174,9 +186,14 @@ export const AdminUserAccessOverviewPage: React.FC = () => { const renderOverviewTab = () => { if (!overview) return null; + const roleById = new Map(overview.roles.map((r) => [r.id, r])); + const globalRoles = overview.roles.filter((r) => r.scope === 'global'); + + const _resolveRoles = (roleIds: string[]): RoleInfo[] => + roleIds.map((id) => roleById.get(id)).filter((r): r is RoleInfo => !!r); + return (
Keine Mandate-Zuordnungen vorhanden.
) : (Keine Rollen direkt am Mandanten.
+ ) : ( +Keine Feature-Instanzen zugewiesen.
+ ) : ( ++ Keine Rollen. +
+ ) : ( +Keine Feature-Instanzen zugewiesen.
- ) : ( -Keine Rollen zugewiesen.
- ) : ( -Beschreibung: {role.description?.de || role.description?.en || '-'}
-Quelle: {role.source === 'mandate' - ? `Mandate: ${role.sourceMandateName}` - : `Feature-Instanz: ${role.sourceInstanceLabel}` - }
+ {globalRoles.length > 0 && ( + <> ++ Nicht an einen Mandanten gebunden. +
++ Beschreibung: {_roleDescriptionLine(role) || '—'} +
+Keine aktiven Feature-Instanzen für diesen Mandanten vorhanden.
+Keine Feature-Instanzen für diesen Mandanten vorhanden.
) : ( )}+ Aufladung des Mandanten-Guthabens ist nur für Mandanten-Administratoren möglich (Menü Administration → Billing). +
+ )} + {canStripeTopUpHere && onCheckout && (