diff --git a/Modules/Documentation/Resources/assets/css/app.css b/Modules/Documentation/Resources/assets/css/app.css new file mode 100644 index 0000000..e575719 --- /dev/null +++ b/Modules/Documentation/Resources/assets/css/app.css @@ -0,0 +1,624 @@ +:root { + --doc-ink: #18181b; + --doc-muted: #71717a; + --doc-line: #e4e4e7; + --doc-panel: #ffffff; + --doc-soft: #f4f4f5; + --doc-brand: #2563eb; + --doc-brand-dark: #1d4ed8; + --doc-accent: #059669; + --doc-danger: #dc2626; + --doc-warning: #d97706; + --doc-radius: 22px; + --doc-shadow: 0 18px 55px rgba(15, 23, 42, .10); +} + +html.dark { + --doc-ink: #f4f4f5; + --doc-muted: #a1a1aa; + --doc-line: #3f3f46; + --doc-panel: #18181b; + --doc-soft: #09090b; + --doc-shadow: 0 18px 55px rgba(0, 0, 0, .28); +} + +body.documentation-module { + margin: 0; + background: #ffffff; + color: var(--doc-ink); + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + min-height: 100vh; +} + +html.dark body.documentation-module { + background: #09090b; +} + +.doc-shell { + max-width: 1800px; + margin: 0 auto; + padding: 0; +} + +.doc-page { + margin: 0 auto; + max-width: 1800px; + padding: 0; + display: grid; + gap: 1.5rem; +} + +.doc-hero-wrap { + overflow: hidden; + border: 1px solid var(--doc-line); + border-radius: 1.5rem; + background: var(--doc-panel); + box-shadow: 0 1px 2px rgba(15, 23, 42, .06); +} + +.doc-hero { + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1.75rem 1.5rem; + color: #ffffff; + background: linear-gradient(135deg, #020617 0%, #0f172a 48%, #172554 100%); +} + +.doc-hero-row { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.doc-hero h1 { + margin: .5rem 0 0; + font-size: clamp(1.875rem, 4vw, 3rem); + line-height: 1; + font-weight: 700; + letter-spacing: -.04em; +} + +.doc-eyebrow { + margin: 0; + color: #bfdbfe; + text-transform: uppercase; + font-size: .75rem; + letter-spacing: .28em; + font-weight: 700; +} + +.doc-hero p { + max-width: 48rem; + margin: .5rem 0 0; + color: rgba(219, 234, 254, .9); + font-size: .875rem; + line-height: 1.65; +} + +.doc-actions { + display: flex; + flex-wrap: wrap; + gap: .5rem; +} + +.doc-button { + border: 1px solid transparent; + border-radius: .75rem; + padding: .625rem .875rem; + font-weight: 700; + font-size: .875rem; + line-height: 1.25rem; + text-decoration: none; + display: inline-flex; + align-items: center; + justify-content: center; + gap: .5rem; + cursor: pointer; + transition: background-color .15s ease, border-color .15s ease, transform .15s ease, box-shadow .15s ease; +} + +.doc-button:hover { + transform: translateY(-1px); +} + +.doc-button.primary, +.doc-button.register, +.doc-button.dark { + background: #2563eb; + color: #ffffff; + border-color: #2563eb; + box-shadow: 0 8px 18px rgba(37, 99, 235, .22); +} + +.doc-button.primary:hover, +.doc-button.register:hover, +.doc-button.dark:hover { + background: #1d4ed8; + border-color: #1d4ed8; +} + +.doc-button.secondary, +.doc-button.category, +.doc-button.settings { + background: #7c3aed; + color: #ffffff; + border-color: #7c3aed; + box-shadow: 0 8px 18px rgba(124, 58, 237, .22); +} + +.doc-button.secondary:hover, +.doc-button.category:hover, +.doc-button.settings:hover { + background: #6d28d9; + border-color: #6d28d9; +} + +.doc-button.light, +.doc-button.cancel, +.doc-button.warning { + background: #f59e0b; + color: #ffffff; + border-color: #f59e0b; + box-shadow: 0 8px 18px rgba(245, 158, 11, .22); +} + +.doc-button.light:hover, +.doc-button.cancel:hover, +.doc-button.warning:hover { + background: #d97706; + border-color: #d97706; +} + +.doc-button.success { + background: #059669; + color: #ffffff; + border-color: #059669; + box-shadow: 0 8px 18px rgba(5, 150, 105, .22); +} + +.doc-button.success:hover { + background: #047857; + border-color: #047857; +} + +.doc-button.danger { + background: #dc2626; + color: #ffffff; + border-color: #dc2626; + box-shadow: 0 8px 18px rgba(220, 38, 38, .22); +} + +.doc-button.danger:hover { + background: #b91c1c; + border-color: #b91c1c; +} + +.doc-card { + background: var(--doc-panel); + border: 1px solid var(--doc-line); + border-radius: 1rem; + box-shadow: 0 1px 2px rgba(15, 23, 42, .06); + overflow: hidden; +} + +.doc-card-header { + padding: 1rem 1.25rem; + border-bottom: 1px solid var(--doc-line); + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; +} + +.doc-card-body { + padding: 1.25rem; +} + +.doc-grid { + display: grid; + gap: 1rem; +} + +.doc-grid.stats { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.doc-stat { + padding: 1.25rem; +} + +.doc-stat strong { + display: block; + font-size: 1.875rem; + line-height: 2.25rem; + letter-spacing: -.04em; + color: var(--doc-ink); +} + +.doc-stat span { + color: var(--doc-muted); + font-size: .75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: .08em; +} + +.doc-table { + width: 100%; + border-collapse: collapse; +} + +.doc-table th { + text-align: left; + font-size: .75rem; + text-transform: uppercase; + letter-spacing: .08em; + color: var(--doc-muted); + padding: .75rem; + border-bottom: 1px solid var(--doc-line); +} + +.doc-table td { + padding: .875rem .75rem; + border-bottom: 1px solid var(--doc-line); + vertical-align: top; +} + +.doc-table a, +.doc-link { + color: #1d4ed8; + font-weight: 700; + text-decoration: none; +} + +html.dark .doc-table a, +html.dark .doc-link { + color: #93c5fd; +} + +.doc-badge { + display: inline-flex; + align-items: center; + border-radius: 999px; + padding: .25rem .625rem; + font-size: .75rem; + font-weight: 800; + background: #dbeafe; + color: #1e40af; + border: 1px solid #bfdbfe; +} + +.doc-badge.published { + background: #ecfdf5; + color: #065f46; + border-color: #a7f3d0; +} + +.doc-badge.draft { + background: #f4f4f5; + color: #52525b; + border-color: #d4d4d8; +} + +.doc-badge.archived { + background: #fef2f2; + color: #991b1b; + border-color: #fecaca; +} + +html.dark .doc-badge { + background: rgba(37, 99, 235, .18); + color: #bfdbfe; + border-color: rgba(59, 130, 246, .35); +} + +html.dark .doc-badge.published { + background: rgba(5, 150, 105, .18); + color: #a7f3d0; + border-color: rgba(16, 185, 129, .35); +} + +html.dark .doc-badge.draft { + background: rgba(113, 113, 122, .18); + color: #d4d4d8; + border-color: rgba(161, 161, 170, .30); +} + +html.dark .doc-badge.archived { + background: rgba(220, 38, 38, .16); + color: #fecaca; + border-color: rgba(248, 113, 113, .32); +} + +.doc-form-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 1rem; +} + +.doc-field.full { + grid-column: 1 / -1; +} + +.doc-label { + display: block; + font-weight: 700; + margin-bottom: .5rem; + font-size: .875rem; + color: var(--doc-ink); +} + +.doc-input, +.doc-select, +.doc-textarea { + width: 100%; + box-sizing: border-box; + border: 1px solid #d4d4d8; + border-radius: .75rem; + padding: .75rem .875rem; + background: #ffffff; + color: #18181b; + font: inherit; + box-shadow: 0 1px 2px rgba(15, 23, 42, .04); +} + +.doc-input:focus, +.doc-select:focus, +.doc-textarea:focus { + outline: 2px solid rgba(37, 99, 235, .24); + border-color: #2563eb; +} + +html.dark .doc-input, +html.dark .doc-select, +html.dark .doc-textarea { + background: #09090b; + color: #f4f4f5; + border-color: #3f3f46; +} + +.doc-textarea { + min-height: 360px; + line-height: 1.6; + resize: vertical; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} + +.doc-editor { + border: 1px solid #d4d4d8; + border-radius: 1rem; + overflow: hidden; + background: #ffffff; +} + +html.dark .doc-editor { + background: #09090b; + border-color: #3f3f46; +} + +.doc-editor .doc-textarea { + border: 0; + border-radius: 0; + min-height: 420px; +} + +.doc-editor-toolbar { + display: flex; + flex-wrap: wrap; + gap: .375rem; + padding: .625rem; + background: #f4f4f5; + border-bottom: 1px solid var(--doc-line); +} + +html.dark .doc-editor-toolbar { + background: #18181b; +} + +.doc-editor-toolbar button { + border: 1px solid #d4d4d8; + background: #ffffff; + color: #18181b; + border-radius: .625rem; + padding: .45rem .625rem; + font-weight: 800; + cursor: pointer; +} + +html.dark .doc-editor-toolbar button { + border-color: #3f3f46; + background: #09090b; + color: #f4f4f5; +} + +.doc-editor-toolbar button:hover, +.doc-editor-toolbar button:focus { + border-color: #2563eb; + outline: 2px solid rgba(37, 99, 235, .20); +} + +.editor-help { + padding: 0 .875rem .75rem; + margin: -.25rem 0 0; + font-size: .8125rem; +} + +.doc-error { + color: #dc2626; + font-size: .8125rem; + margin-top: .375rem; +} + +.doc-alert { + border-radius: 1rem; + padding: .875rem 1rem; + background: #ecfdf5; + color: #065f46; + border: 1px solid #a7f3d0; + font-size: .875rem; + font-weight: 700; +} + +html.dark .doc-alert { + background: #022c22; + color: #a7f3d0; + border-color: #065f46; +} + +.doc-warning { + border-radius: 1rem; + padding: .875rem 1rem; + background: #fffbeb; + color: #92400e; + border: 1px solid #fcd34d; + font-size: .875rem; +} + +html.dark .doc-warning { + background: #451a03; + color: #fef3c7; + border-color: #92400e; +} + +.doc-filter { + display: grid; + grid-template-columns: 1fr 180px 220px auto; + gap: .75rem; + margin-bottom: 1.125rem; +} + +.doc-content { + line-height: 1.75; + font-size: 1rem; +} + +.doc-content h1, +.doc-content h2, +.doc-content h3 { + line-height: 1.2; + letter-spacing: -.025em; + margin: 1.4em 0 .55em; +} + +.doc-content h1 { + font-size: 2rem; +} + +.doc-content h2 { + font-size: 1.55rem; + border-bottom: 1px solid var(--doc-line); + padding-bottom: .35rem; +} + +.doc-content h3 { + font-size: 1.25rem; +} + +.doc-content p, +.doc-content ul, +.doc-content ol, +.doc-content blockquote, +.doc-content pre { + margin: 0 0 1rem; +} + +.doc-content code { + background: #f1f5f9; + border-radius: .4375rem; + padding: .14rem .32rem; + font-size: .92em; +} + +html.dark .doc-content code { + background: #1e293b; + color: #e2e8f0; +} + +.doc-content pre { + overflow-x: auto; + background: #0f172a; + color: #e2e8f0; + border-radius: 1rem; + padding: 1rem; +} + +.doc-content pre code { + background: transparent; + color: inherit; + padding: 0; +} + +.doc-content blockquote { + border-left: 4px solid #2563eb; + padding-left: 1rem; + color: var(--doc-muted); +} + +.doc-content table { + width: 100%; + border-collapse: collapse; + margin: 1rem 0; +} + +.doc-content th, +.doc-content td { + border: 1px solid var(--doc-line); + padding: .625rem .75rem; + text-align: left; +} + +.doc-muted { + color: var(--doc-muted); +} + +.doc-attachment-list { + display: grid; + gap: .625rem; +} + +.doc-attachment-item { + display: flex; + justify-content: space-between; + align-items: center; + gap: .875rem; + border: 1px solid var(--doc-line); + border-radius: .875rem; + padding: .75rem .875rem; + background: var(--doc-panel); +} + +@media (min-width: 1024px) { + .doc-hero-row { + flex-direction: row; + align-items: center; + justify-content: space-between; + } +} + +@media (max-width: 860px) { + .doc-grid.stats { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .doc-filter, + .doc-form-grid { + grid-template-columns: 1fr; + } + + .doc-table { + display: block; + overflow-x: auto; + } +} + +@media (max-width: 560px) { + .doc-grid.stats { + grid-template-columns: 1fr; + } + + .doc-hero { + padding: 1.375rem; + border-radius: 1.375rem; + } +} diff --git a/Modules/Documentation/Resources/assets/js/app.js b/Modules/Documentation/Resources/assets/js/app.js new file mode 100644 index 0000000..6a548ed --- /dev/null +++ b/Modules/Documentation/Resources/assets/js/app.js @@ -0,0 +1,24 @@ +document.addEventListener('click', function(event) { + const button = event.target.closest('[data-md-before]'); + + if (!button) { + return; + } + + const editor = button.closest('[data-documentation-editor]'); + const textarea = editor ? editor.querySelector('[data-markdown-textarea]') : null; + + if (!textarea) { + return; + } + + const before = button.getAttribute('data-md-before') || ''; + const after = button.getAttribute('data-md-after') || ''; + const start = textarea.selectionStart || 0; + const end = textarea.selectionEnd || 0; + const selected = textarea.value.substring(start, end); + const replacement = before + selected + after; + + textarea.setRangeText(replacement, start, end, 'end'); + textarea.focus(); +}); diff --git a/Modules/Documentation/Resources/views/layouts/master.blade.php b/Modules/Documentation/Resources/views/layouts/master.blade.php index ca1af18..be3fd81 100644 --- a/Modules/Documentation/Resources/views/layouts/master.blade.php +++ b/Modules/Documentation/Resources/views/layouts/master.blade.php @@ -1,659 +1,8 @@ -
@yield('content')
-
diff --git a/Modules/Documentation/package.json b/Modules/Documentation/package.json new file mode 100644 index 0000000..44e4a84 --- /dev/null +++ b/Modules/Documentation/package.json @@ -0,0 +1,4 @@ +{ + "private": true, + "type": "module" +} diff --git a/Modules/Helpdesk/Livewire/SettingsManager.php b/Modules/Helpdesk/Livewire/SettingsManager.php index f31a4e7..cd80dbc 100644 --- a/Modules/Helpdesk/Livewire/SettingsManager.php +++ b/Modules/Helpdesk/Livewire/SettingsManager.php @@ -32,6 +32,8 @@ final class SettingsManager extends Component { public string $section = 'statuses'; + public bool $teamModalOpen = false; + /** @var array */ public array $statusForm = ['id' => null, 'name' => '', 'slug' => '', 'color' => 'gray', 'sort_order' => 0, 'is_closed' => false, 'is_default' => false]; @@ -321,6 +323,7 @@ final class SettingsManager extends Component } $this->resetTeamForm(); + $this->teamModalOpen = false; $this->section = 'teams'; session()->flash('success', 'Team saved successfully.'); @@ -333,6 +336,22 @@ final class SettingsManager extends Component $this->teamForm['user_ids'] = $team->users->pluck('id')->map(static fn ($id): int => (int) $id)->all(); $this->teamForm['color'] = Color::normalise($this->teamForm['color'] ?? 'indigo'); $this->section = 'teams'; + $this->teamModalOpen = true; + } + + public function openCreateTeamModal(): void + { + $this->resetValidation(); + $this->resetTeamForm(); + $this->section = 'teams'; + $this->teamModalOpen = true; + } + + public function closeTeamModal(): void + { + $this->resetValidation(); + $this->resetTeamForm(); + $this->teamModalOpen = false; } public function deleteTeam(int $id): void diff --git a/Modules/UserManagement/Resources/assets/css/app.css b/Modules/UserManagement/Resources/assets/css/app.css new file mode 100644 index 0000000..7d53a00 --- /dev/null +++ b/Modules/UserManagement/Resources/assets/css/app.css @@ -0,0 +1,4 @@ +@import "tailwindcss"; + +@source "../../../../../resources/themes/backend/views/modules/user-management/**/*.blade.php"; +@source "../../../Http/**/*.php"; diff --git a/Modules/UserManagement/Resources/assets/js/app.js b/Modules/UserManagement/Resources/assets/js/app.js new file mode 100644 index 0000000..e3a62db --- /dev/null +++ b/Modules/UserManagement/Resources/assets/js/app.js @@ -0,0 +1 @@ +// User Management module JavaScript entry point. diff --git a/Modules/UserManagement/package.json b/Modules/UserManagement/package.json new file mode 100644 index 0000000..44e4a84 --- /dev/null +++ b/Modules/UserManagement/package.json @@ -0,0 +1,4 @@ +{ + "private": true, + "type": "module" +} diff --git a/package.json b/package.json index 9477b0b..dbc3e03 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,10 @@ "type": "module", "scripts": { "build": "vite build --config ./vite.config.js", - "dev": "vite --config ./vite.config.js" + "dev": "vite --config ./vite.config.js", + "modules:install": "node scripts/install-module-dependencies.mjs", + "prebuild": "npm run modules:install", + "predev": "npm run modules:install" }, "dependencies": { "@fortawesome/fontawesome-free": "^7.0.0", diff --git a/resources/themes/backend/views/components/helpdesk/settings-section.blade.php b/resources/themes/backend/views/components/helpdesk/settings-section.blade.php index 3074835..d3c6829 100644 --- a/resources/themes/backend/views/components/helpdesk/settings-section.blade.php +++ b/resources/themes/backend/views/components/helpdesk/settings-section.blade.php @@ -15,17 +15,24 @@

{{ $description }}

- - - +
+ @isset($actions) + {{ $actions }} + @endisset + + + +
-
-
- {{ $form }} + @isset($form) +
+
+ {{ $form }} +
-
+ @endisset
{{ $slot }} diff --git a/resources/themes/backend/views/modules/helpdesk/livewire/settings-manager.blade.php b/resources/themes/backend/views/modules/helpdesk/livewire/settings-manager.blade.php index 4bf374c..449c211 100644 --- a/resources/themes/backend/views/modules/helpdesk/livewire/settings-manager.blade.php +++ b/resources/themes/backend/views/modules/helpdesk/livewire/settings-manager.blade.php @@ -223,73 +223,114 @@ @if ($section === 'teams') - -
-
-
-
- -
-
- -
- -
- -
-
-
-
-
-
Team members
-
Select one or more users to assign to this team.
-
-
- {{ count($teamForm['user_ids'] ?? []) }} selected -
-
+ + + + Create team + + -
- @forelse ($teamUsers as $teamUser) - - @empty -
No users are available.
- @endforelse -
+ + + + +
+
+ + {{ filled($teamForm['id'] ?? null) ? 'Edit team' : 'Create team' }} + + + Configure team details and choose the users who belong to it. +
-