From 138a028537eb4f0203de252dba53b81769ea9edd Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Wed, 26 Aug 2026 12:38:12 +0100 Subject: [PATCH] structure updates --- Modules/Helpdesk/Livewire/SettingsManager.php | 9 +- Modules/Helpdesk/Resources/assets/css/app.css | 375 ++++++++++++++++++ app/Models/User.php | 11 + resources/themes/backend/assets/css/app.css | 17 - .../themes/backend/assets/css/helpdesk.css | 210 ++++++++++ .../components/desktop-user-menu.blade.php | 99 +++-- .../helpdesk/colour-select.blade.php | 101 +++-- .../components/settings/layout.blade.php | 2 +- .../backend/views/layouts/app.blade.php | 7 +- .../views/layouts/app/sidebar.blade.php | 86 ---- .../views/layouts/auth/simple.blade.php | 2 +- .../helpdesk/livewire/kanban-board.blade.php | 71 ++-- .../livewire/settings-manager.blade.php | 28 +- .../helpdesk/livewire/ticket-index.blade.php | 71 +++- 14 files changed, 819 insertions(+), 270 deletions(-) delete mode 100644 resources/themes/backend/assets/css/app.css delete mode 100644 resources/themes/backend/views/layouts/app/sidebar.blade.php diff --git a/Modules/Helpdesk/Livewire/SettingsManager.php b/Modules/Helpdesk/Livewire/SettingsManager.php index 746f16d..a2237a0 100644 --- a/Modules/Helpdesk/Livewire/SettingsManager.php +++ b/Modules/Helpdesk/Livewire/SettingsManager.php @@ -11,6 +11,7 @@ use Illuminate\Database\QueryException; use Illuminate\Support\Facades\Schema; use Illuminate\Support\Str; use Illuminate\Validation\ValidationException; +use Livewire\Attributes\Layout; use Livewire\Component; use Livewire\WithPagination; use Modules\Helpdesk\Models\AutomationRule; @@ -1034,9 +1035,12 @@ final class SettingsManager extends Component $this->checklistTemplateForm = ['id' => null, 'category_id' => null, 'project_id' => null, 'name' => '', 'slug' => '', 'items_text' => '', 'is_active' => true]; } + #[Layout('layouts.app')] public function render(): View { - return view('modules.helpdesk.livewire.settings-manager', [ + return view()->file( + resource_path('themes/backend/views/modules/helpdesk/livewire/settings-manager.blade.php'), + [ 'statuses' => $this->orderedStatuses(), 'priorities' => $this->orderedPriorities(), 'categories' => $this->orderedCategories(), @@ -1088,7 +1092,8 @@ final class SettingsManager extends Component ->orderBy('name') ->paginate(10, ['*'], 'checksPage'), 'colourOptions' => Color::options(), - ])->layout('layouts.app'); + ] + ); } private function orderedStatuses(): mixed diff --git a/Modules/Helpdesk/Resources/assets/css/app.css b/Modules/Helpdesk/Resources/assets/css/app.css index ef97daf..d6f870b 100644 --- a/Modules/Helpdesk/Resources/assets/css/app.css +++ b/Modules/Helpdesk/Resources/assets/css/app.css @@ -1017,3 +1017,378 @@ span.flatpickr-weekday { padding-inline: 0.25rem; } } + + +/* Professional coloured headers for Helpdesk settings modals */ +.hd-settings-modal-header { + position: relative; + overflow: hidden; + margin-bottom: 1.35rem; + padding: 1.05rem 1.2rem; + border: 1px solid rgba(255, 255, 255, 0.10); + border-radius: 0.9rem; + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.08), + 0 12px 30px rgba(0, 0, 0, 0.14); +} + +.hd-settings-modal-header::after { + content: ""; + position: absolute; + top: -3.5rem; + right: -2rem; + width: 11rem; + height: 11rem; + border-radius: 9999px; + background: rgba(255, 255, 255, 0.08); + filter: blur(1px); + pointer-events: none; +} + +.hd-settings-modal-header [data-flux-heading], +.hd-settings-modal-header h1, +.hd-settings-modal-header h2, +.hd-settings-modal-header h3 { + position: relative; + z-index: 1; + color: #fff !important; + font-weight: 700; + letter-spacing: -0.015em; +} + +.hd-settings-modal-header [data-flux-subheading], +.hd-settings-modal-header p { + position: relative; + z-index: 1; + color: rgba(255, 255, 255, 0.74) !important; +} + +.hd-settings-modal-header--status { + background: linear-gradient(135deg, #075985 0%, #0369a1 48%, #0e7490 100%); +} + +.hd-settings-modal-header--priority { + background: linear-gradient(135deg, #7f1d1d 0%, #b91c1c 48%, #c2410c 100%); +} + +.hd-settings-modal-header--category { + background: linear-gradient(135deg, #3730a3 0%, #4f46e5 48%, #7c3aed 100%); +} + +.hd-settings-modal-header--team { + background: linear-gradient(135deg, #1e3a8a 0%, #1d4ed8 48%, #0369a1 100%); +} + +.hd-settings-modal-header--project { + background: linear-gradient(135deg, #155e75 0%, #0e7490 48%, #0f766e 100%); +} + +.hd-settings-modal-header--board { + background: linear-gradient(135deg, #78350f 0%, #b45309 48%, #d97706 100%); +} + +.hd-settings-modal-header--git { + background: linear-gradient(135deg, #1f2937 0%, #374151 52%, #475569 100%); +} + +.hd-settings-modal-header--workflow { + background: linear-gradient(135deg, #115e59 0%, #0f766e 48%, #047857 100%); +} + +.hd-settings-modal-header--canned { + background: linear-gradient(135deg, #164e63 0%, #0891b2 48%, #0284c7 100%); +} + +.hd-settings-modal-header--sla { + background: linear-gradient(135deg, #7c2d12 0%, #c2410c 48%, #ea580c 100%); +} + +.hd-settings-modal-header--automation { + background: linear-gradient(135deg, #581c87 0%, #7e22ce 48%, #9333ea 100%); +} + +.hd-settings-modal-header--checklist { + background: linear-gradient(135deg, #365314 0%, #4d7c0f 48%, #65a30d 100%); +} + +.hd-settings-modal-header--tag { + background: linear-gradient(135deg, #831843 0%, #be185d 48%, #db2777 100%); +} + +.hd-settings-modal-header--product { + background: linear-gradient(135deg, #312e81 0%, #4338ca 48%, #6366f1 100%); +} + +@media (max-width: 640px) { + .hd-settings-modal-header { + padding: 0.95rem 1rem; + border-radius: 0.8rem; + } +} + + +/* Settings modal shell alignment + typography */ +.hd-settings-modal, +.hd-settings-modal * { + font-family: "Instrument Sans", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important; +} + +.hd-settings-modal { + text-align: left; +} + +/* + * Flux supplies the modal body padding. Pull the coloured banner back over + * that padding so it visually becomes the modal's own top header rather than + * looking like a card floating inside the dialog. + */ +.hd-settings-modal .hd-settings-modal-header { + margin-top: -1.5rem; + margin-right: -1.5rem; + margin-bottom: 1.5rem; + margin-left: -1.5rem; + padding: 1.35rem 1.5rem 1.3rem; + border-top: 0; + border-right: 0; + border-left: 0; + border-radius: 1rem 1rem 0 0; + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.08), + 0 10px 26px rgba(0, 0, 0, 0.16); +} + +.hd-settings-modal .hd-settings-modal-header [data-flux-heading], +.hd-settings-modal .hd-settings-modal-header h1, +.hd-settings-modal .hd-settings-modal-header h2, +.hd-settings-modal .hd-settings-modal-header h3 { + margin: 0; + font-family: inherit !important; + font-size: 1.25rem !important; + font-weight: 700 !important; + line-height: 1.35 !important; + letter-spacing: -0.02em !important; +} + +.hd-settings-modal .hd-settings-modal-header [data-flux-subheading], +.hd-settings-modal .hd-settings-modal-header p { + margin-top: 0.45rem !important; + margin-bottom: 0 !important; + font-family: inherit !important; + font-size: 0.95rem !important; + font-weight: 400 !important; + line-height: 1.5 !important; + letter-spacing: 0 !important; +} + +.hd-settings-modal input, +.hd-settings-modal select, +.hd-settings-modal textarea, +.hd-settings-modal button { + font-family: inherit !important; +} + +@media (max-width: 640px) { + .hd-settings-modal .hd-settings-modal-header { + margin-top: -1rem; + margin-right: -1rem; + margin-left: -1rem; + padding: 1.1rem 1rem; + border-radius: 0.85rem 0.85rem 0 0; + } +} + +/* Kanban-style settings form rhythm */ +.hd-settings-modal { + color: rgb(244 244 245); +} + +.hd-settings-modal :is(label, [data-flux-label]) { + font-family: "Instrument Sans", ui-sans-serif, system-ui, sans-serif !important; + font-size: 0.95rem !important; + font-weight: 600 !important; + line-height: 1.35 !important; + letter-spacing: -0.01em !important; + text-transform: none !important; +} + +.hd-settings-modal :is(input:not([type="checkbox"]):not([type="radio"]):not([type="color"]), select, textarea) { + min-height: 3rem; + font-family: "Instrument Sans", ui-sans-serif, system-ui, sans-serif !important; + font-size: 1rem !important; + font-weight: 500 !important; + line-height: 1.4 !important; + letter-spacing: -0.01em !important; + color: rgb(244 244 245) !important; + background: rgb(39 39 42) !important; + border-color: rgb(82 82 91) !important; + border-radius: 0.75rem !important; +} + +.hd-settings-modal :is(input, select, textarea):focus { + border-color: rgb(59 130 246) !important; + box-shadow: 0 0 0 3px rgb(59 130 246 / 0.16) !important; +} + +.hd-settings-modal textarea { + min-height: 7.5rem; + padding-top: 0.85rem !important; + padding-bottom: 0.85rem !important; +} + +.hd-settings-modal button, +.hd-settings-modal [role="button"] { + font-family: "Instrument Sans", ui-sans-serif, system-ui, sans-serif !important; + font-weight: 650 !important; + letter-spacing: -0.01em !important; +} + +/* Shared horizontal colour control for Status, Priority, Category, Team, Project and Tag editors. */ +.hd-colour-control { + width: 100%; + padding: 1rem 1.1rem; + border: 1px solid rgb(63 63 70); + border-radius: 0.9rem; + background: rgb(24 24 27 / 0.78); + box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.025); +} + +.hd-colour-control__label { + margin-bottom: 0.65rem; + font-family: "Instrument Sans", ui-sans-serif, system-ui, sans-serif; + font-size: 0.95rem; + font-weight: 650; + line-height: 1.35; + letter-spacing: -0.01em; + color: rgb(228 228 231); + text-transform: none; +} + +.hd-colour-control__row { + display: grid; + grid-template-columns: minmax(18rem, 1fr) minmax(7.5rem, auto) auto 3.25rem minmax(8rem, 10rem); + align-items: center; + gap: 0.8rem; +} + +.hd-colour-control__select, +.hd-colour-control__value { + height: 3rem; + min-width: 0; + border: 1px solid rgb(82 82 91); + border-radius: 0.75rem; + background: rgb(39 39 42); + color: rgb(244 244 245); + font-family: "Instrument Sans", ui-sans-serif, system-ui, sans-serif; + font-size: 1rem; + font-weight: 600; + outline: none; + transition: border-color 150ms ease, box-shadow 150ms ease; +} + +.hd-colour-control__select { + width: 100%; + padding: 0 0.95rem; +} + +.hd-colour-control__value { + width: 100%; + padding: 0 0.9rem; +} + +.hd-colour-control__select:focus, +.hd-colour-control__value:focus { + border-color: rgb(59 130 246); + box-shadow: 0 0 0 3px rgb(59 130 246 / 0.16); +} + +.hd-colour-control__badge { + display: inline-flex; + height: 3rem; + min-width: 7.5rem; + align-items: center; + justify-content: center; + padding: 0 1rem; + border-radius: 0.75rem; + font-family: "Instrument Sans", ui-sans-serif, system-ui, sans-serif; + font-size: 0.95rem; + font-weight: 700; + box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.12); +} + +.hd-colour-control__swatches { + display: flex; + flex-wrap: nowrap; + align-items: center; + gap: 0.45rem; +} + +.hd-colour-control__swatch { + width: 1.75rem; + height: 1.75rem; + flex: 0 0 auto; + border: 2px solid rgb(228 228 231 / 0.86); + border-radius: 9999px; + box-shadow: 0 0 0 1px rgb(0 0 0 / 0.28); + transition: transform 120ms ease, box-shadow 120ms ease; +} + +.hd-colour-control__swatch:hover { + transform: translateY(-1px) scale(1.06); + box-shadow: 0 0 0 3px rgb(59 130 246 / 0.14); +} + +.hd-colour-control__picker { + width: 3.25rem; + height: 3rem; + padding: 0.35rem; + cursor: pointer; + border: 1px solid rgb(82 82 91); + border-radius: 0.75rem; + background: rgb(39 39 42); +} + +@media (max-width: 1180px) { + .hd-colour-control__row { + grid-template-columns: minmax(16rem, 1fr) minmax(7rem, auto) auto; + } + + .hd-colour-control__picker, + .hd-colour-control__value { + grid-row: 2; + } + + .hd-colour-control__value { + grid-column: 2 / 4; + max-width: 13rem; + } +} + +@media (max-width: 720px) { + .hd-colour-control { + padding: 0.9rem; + } + + .hd-colour-control__row { + display: flex; + flex-wrap: wrap; + } + + .hd-colour-control__select { + flex: 1 1 100%; + } + + .hd-colour-control__badge { + flex: 1 1 8rem; + } + + .hd-colour-control__swatches { + order: 5; + width: 100%; + overflow-x: auto; + padding-top: 0.15rem; + } + + .hd-colour-control__value { + flex: 1 1 8rem; + } +} diff --git a/app/Models/User.php b/app/Models/User.php index e2e6a48..18e840d 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -51,6 +51,17 @@ class User extends Authenticatable implements PasskeyUser } + + public function gravatarUrl(int $size = 160): string + { + $email = strtolower(trim((string) $this->email)); + $hash = hash('sha256', $email); + + return 'https://www.gravatar.com/avatar/'.$hash + .'?s='.max(1, min($size, 2048)) + .'&d=404&r=g'; + } + public function roles(): BelongsToMany { return $this->belongsToMany( diff --git a/resources/themes/backend/assets/css/app.css b/resources/themes/backend/assets/css/app.css deleted file mode 100644 index e4cd6a9..0000000 --- a/resources/themes/backend/assets/css/app.css +++ /dev/null @@ -1,17 +0,0 @@ -@import 'tailwindcss'; -@import '@fortawesome/fontawesome-free/css/all.min.css'; -@import '../../../../../vendor/livewire/flux/dist/flux.css'; -@import './helpdesk.css'; - -@source '../../views/**/*.blade.php'; -@source '../../../../../Modules/Helpdesk/**/*.php'; -@source '../../../../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php'; -@source '../../../../../vendor/livewire/flux/stubs/**/*.blade.php'; -@source '../../../../../vendor/livewire/flux-pro/stubs/**/*.blade.php'; - -@custom-variant dark (&:where(.dark, .dark *)); - -@theme { - --font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, - 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; -} diff --git a/resources/themes/backend/assets/css/helpdesk.css b/resources/themes/backend/assets/css/helpdesk.css index 18075ad..b40c181 100644 --- a/resources/themes/backend/assets/css/helpdesk.css +++ b/resources/themes/backend/assets/css/helpdesk.css @@ -1337,3 +1337,213 @@ span.flatpickr-weekday { @media(max-width:1100px){.hd-metric-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.hd-dashboard-grid{grid-template-columns:1fr}.hd-priority-body{min-height:240px}.hd-top-search{display:none}} @media(max-width:1023px){:root{--hd-sidebar-width:300px}.hd-topbar{display:flex}.hd-topbar-brand{width:auto;border-right:0}.hd-topbar-main{flex:1;padding-left:8px}.hd-mobile-menu-button{display:inline-flex}.hd-shell-sidebar{transform:translateX(-102%);width:min(84vw,320px);box-shadow:15px 0 40px rgba(0,0,0,.35)}.hd-sidebar-open .hd-shell-sidebar{transform:translateX(0)}.hd-sidebar-resizer{display:none}.hd-shell-main{margin-left:0;padding:calc(var(--hd-topbar-height) + 20px) 18px 25px}.hd-sidebar-backdrop{position:fixed;z-index:55;inset:var(--hd-topbar-height) 0 0 0;background:rgba(0,0,0,.52);border:0}.hd-sidebar-open .hd-sidebar-backdrop{display:block}} @media(max-width:700px){.hd-account-copy{display:none}.hd-topbar-main{padding-right:10px}.hd-topbar-brand{padding-left:10px;padding-right:8px}.hd-brand-name{font-size:.95rem}.hd-shell-main{padding-left:10px;padding-right:10px}.hd-dashboard-heading{align-items:flex-start;flex-direction:column}.hd-range-select{width:100%}.hd-metric-grid{grid-template-columns:1fr}.hd-dashboard-card{border-radius:10px}.hd-priority-body{grid-template-columns:1fr}.hd-latest-list>a{grid-template-columns:65px minmax(0,1fr);gap:10px}.hd-latest-list time,.hd-latest-list .hd-status-mini{display:none}.hd-page-hero{align-items:flex-start;flex-direction:column;padding:18px}.hd-page-hero .hd-action{width:100%}.hd-toolbar{flex-direction:column}.hd-form-grid,.hd-role-options{grid-template-columns:1fr}.hd-action{min-height:44px}} + + +/* Dark account menu + Gravatar */ +.hd-account-menu { + position: relative; + display: inline-block; + color: #f8fafc; +} + +.hd-account-menu > summary { + list-style: none; +} + +.hd-account-menu > summary::-webkit-details-marker { + display: none; +} + +.hd-account-menu[open] .hd-account-button { + background: #202732; + border-color: #3a4554; +} + +.hd-account-menu[open] .hd-account-chevron { + transform: rotate(180deg); +} + +.hd-account-chevron { + font-size: .75rem; + transition: transform .16s ease; +} + +.hd-user-avatar--gravatar { + position: relative; + overflow: hidden; +} + +.hd-user-avatar--gravatar img { + position: absolute; + inset: 0; + z-index: 2; + width: 100%; + height: 100%; + display: block; + border-radius: inherit; + object-fit: cover; + background: #252c36; +} + +.hd-user-avatar-fallback { + position: relative; + z-index: 1; + display: inline-flex; + width: 100%; + height: 100%; + align-items: center; + justify-content: center; +} + +.hd-user-avatar--large { + width: 46px; + height: 46px; + flex-basis: 46px; + border-radius: 12px; +} + +.hd-account-dropdown { + position: absolute; + top: calc(100% + 10px); + right: 0; + z-index: 120; + width: min(310px, calc(100vw - 24px)); + overflow: hidden; + border: 1px solid #333d4a; + border-radius: 14px; + background: + linear-gradient(180deg, rgba(28, 34, 44, .98), rgba(18, 23, 30, .99)); + color: #e5e7eb; + box-shadow: + 0 24px 60px rgba(0, 0, 0, .48), + 0 8px 22px rgba(0, 0, 0, .28), + inset 0 1px 0 rgba(255, 255, 255, .035); +} + +.hd-account-dropdown-user { + display: flex; + align-items: center; + gap: 12px; + padding: 16px; + background: rgba(255, 255, 255, .018); +} + +.hd-account-dropdown-copy { + min-width: 0; + display: grid; + gap: 2px; +} + +.hd-account-dropdown-copy strong { + overflow: hidden; + color: #f8fafc; + font-size: .95rem; + font-weight: 700; + line-height: 1.25; + text-overflow: ellipsis; + white-space: nowrap; +} + +.hd-account-dropdown-copy span { + overflow: hidden; + color: #9ca3af; + font-size: .78rem; + line-height: 1.25; + text-overflow: ellipsis; + white-space: nowrap; +} + +.hd-account-dropdown-copy small { + margin-top: 2px; + color: #60a5fa; + font-size: .7rem; + font-weight: 700; + letter-spacing: .055em; + text-transform: uppercase; +} + +.hd-account-dropdown-divider { + height: 1px; + background: #303846; +} + +.hd-account-dropdown-nav { + display: grid; + gap: 4px; + padding: 8px; +} + +.hd-account-dropdown-nav a, +.hd-account-logout { + width: 100%; + min-height: 42px; + display: flex; + align-items: center; + gap: 11px; + padding: 9px 11px; + border: 1px solid transparent; + border-radius: 9px; + background: transparent; + color: #d7dce4; + font: inherit; + font-size: .86rem; + font-weight: 600; + line-height: 1.25; + text-align: left; + text-decoration: none; + cursor: pointer; + transition: + background .14s ease, + border-color .14s ease, + color .14s ease; +} + +.hd-account-dropdown-nav a i, +.hd-account-logout i { + width: 17px; + color: #8fa0b5; + text-align: center; +} + +.hd-account-dropdown-nav a:hover, +.hd-account-dropdown-nav a:focus-visible { + border-color: #35465c; + background: #263244; + color: #fff; + outline: none; +} + +.hd-account-dropdown-nav a:hover i { + color: #60a5fa; +} + +.hd-account-dropdown form { + padding: 8px; +} + +.hd-account-logout:hover, +.hd-account-logout:focus-visible { + border-color: rgba(239, 68, 68, .32); + background: rgba(127, 29, 29, .24); + color: #fecaca; + outline: none; +} + +.hd-account-logout:hover i, +.hd-account-logout:focus-visible i { + color: #f87171; +} + +.hd-account-menu--legacy .hd-account-dropdown { + left: 0; + right: auto; +} + +@media (max-width: 700px) { + .hd-account-dropdown { + position: fixed; + top: calc(var(--hd-topbar-height) - 2px); + right: 10px; + width: min(310px, calc(100vw - 20px)); + } +} diff --git a/resources/themes/backend/views/components/desktop-user-menu.blade.php b/resources/themes/backend/views/components/desktop-user-menu.blade.php index 7fb3706..2a8684b 100644 --- a/resources/themes/backend/views/components/desktop-user-menu.blade.php +++ b/resources/themes/backend/views/components/desktop-user-menu.blade.php @@ -1,39 +1,66 @@ - - +@php + $user = auth()->user(); +@endphp - -
- -
- {{ auth()->user()->name }} - {{ auth()->user()->email }} + diff --git a/resources/themes/backend/views/components/helpdesk/colour-select.blade.php b/resources/themes/backend/views/components/helpdesk/colour-select.blade.php index 1385c92..7aeda6b 100644 --- a/resources/themes/backend/views/components/helpdesk/colour-select.blade.php +++ b/resources/themes/backend/views/components/helpdesk/colour-select.blade.php @@ -11,64 +11,57 @@ $currentLabel = \Modules\Helpdesk\Support\Color::label($currentValue); @endphp -
class('rounded-xl border border-zinc-200 bg-white p-3 shadow-sm dark:border-zinc-700 dark:bg-zinc-950/50') }}> -
-
- +
class('hd-colour-control') }}> +
{{ $label }}
-
- + @foreach ($options as $key => $option) + + @endforeach + + + + {{ $currentLabel }} + + +
+ @foreach ($options as $key => $option) +
- -
- @foreach ($options as $key => $option) - - @endforeach -
+ Use {{ $option['name'] }} + + @endforeach
-
- + - -
+
diff --git a/resources/themes/backend/views/components/settings/layout.blade.php b/resources/themes/backend/views/components/settings/layout.blade.php index 3a65247..d4fc5e5 100644 --- a/resources/themes/backend/views/components/settings/layout.blade.php +++ b/resources/themes/backend/views/components/settings/layout.blade.php @@ -1,4 +1,4 @@ -
+