phases one of time logged now with log display
This commit is contained in:
@@ -1,51 +1,48 @@
|
||||
<x-layouts::app :title="__('Analytics')">
|
||||
<div class="container-fluid py-4 module-page module-analytics">
|
||||
<div class="container-fluid py-4 module-page module-analytics">
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="Analytics"
|
||||
<x-wms.page-hero eyebrow="Operations" title="Analytics"
|
||||
description="Display KPIs, warehouse performance trends, executive summaries, and OTTC analysis."
|
||||
icon="fa-solid fa-chart-line"
|
||||
/>
|
||||
icon="fa-solid fa-chart-line" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">KPI1 Bar</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for kpi1 bar.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">KPI1 Bar</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for kpi1 bar.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">KPI1 Line</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for kpi1 line.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">KPI1 Line</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for kpi1 line.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">OTTC</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for ottc.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">OTTC</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for ottc.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Warehouse Performance</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for warehouse performance.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Warehouse Performance</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for warehouse performance.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<section class="card border-0 shadow-sm">
|
||||
@@ -53,7 +50,8 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Analytics workbench</h2>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.</p>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers,
|
||||
queries, DataTables, or Livewire.</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
@@ -78,26 +76,38 @@
|
||||
<tr>
|
||||
<td>KPI1 Bar</td>
|
||||
<td>Sample 1</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>KPI1 Line</td>
|
||||
<td>Sample 2</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OTTC</td>
|
||||
<td>Sample 3</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Warehouse Performance</td>
|
||||
<td>Sample 4</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
<x-layouts::app :title="__('Audit')">
|
||||
<div class="container-fluid py-4 module-page module-audit">
|
||||
<div class="container-fluid py-4 module-page module-audit">
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="Audit"
|
||||
<x-wms.page-hero eyebrow="Operations" title="Audit"
|
||||
description="Review activity logs, data changes, interface events, and administrative actions."
|
||||
icon="fa-solid fa-clipboard-check"
|
||||
/>
|
||||
icon="fa-solid fa-clipboard-check" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Activity Log</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for activity log.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Activity Log</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for activity log.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Data Changes</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for data changes.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Data Changes</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for data changes.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">API Audit</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for api audit.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">API Audit</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for api audit.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">User Actions</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for user actions.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">User Actions</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for user actions.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<section class="card border-0 shadow-sm">
|
||||
@@ -53,7 +50,8 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Audit workbench</h2>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.</p>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers,
|
||||
queries, DataTables, or Livewire.</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
@@ -78,26 +76,38 @@
|
||||
<tr>
|
||||
<td>Activity Log</td>
|
||||
<td>Sample 1</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Data Changes</td>
|
||||
<td>Sample 2</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>API Audit</td>
|
||||
<td>Sample 3</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>User Actions</td>
|
||||
<td>Sample 4</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Module AuditTrail</title>
|
||||
|
||||
{{-- Laravel Mix - CSS File --}}
|
||||
{{-- <link rel="stylesheet" href="{{ mix('css/audittrail.css') }}"> --}}
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Module AuditTrail</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);" aria-hidden="true"></div>
|
||||
@yield('content')
|
||||
{{-- Laravel Mix - CSS File --}}
|
||||
{{-- <link rel="stylesheet" href="{{ mix('css/audittrail.css') }}"> --}}
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
|
||||
aria-hidden="true"></div>
|
||||
@yield('content')
|
||||
|
||||
{{-- Laravel Mix - JS File --}}
|
||||
{{-- <script src="{{ mix('js/audittrail.js') }}"></script> --}}
|
||||
</body>
|
||||
|
||||
{{-- Laravel Mix - JS File --}}
|
||||
{{-- <script src="{{ mix('js/audittrail.js') }}"></script> --}}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
<x-layouts::app :title="__('Carrier Management')">
|
||||
<div class="container-fluid py-4 module-page module-carrier-management">
|
||||
<div class="container-fluid py-4 module-page module-carrier-management">
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="Carrier Management"
|
||||
<x-wms.page-hero eyebrow="Operations" title="Carrier Management"
|
||||
description="Maintain carriers, services, and shipment routing master data."
|
||||
icon="fa-solid fa-id-card"
|
||||
/>
|
||||
icon="fa-solid fa-id-card" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Carrier Directory</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for carrier directory.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Carrier Directory</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for carrier directory.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Carrier Service Levels</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for carrier service levels.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Carrier Service Levels</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for carrier service levels.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Carrier Rules</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for carrier rules.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Carrier Rules</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for carrier rules.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Carrier Audit</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for carrier audit.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Carrier Audit</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for carrier audit.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<section class="card border-0 shadow-sm">
|
||||
@@ -53,7 +50,8 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Carrier Management workbench</h2>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.</p>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers,
|
||||
queries, DataTables, or Livewire.</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
@@ -78,26 +76,38 @@
|
||||
<tr>
|
||||
<td>Carrier Directory</td>
|
||||
<td>Sample 1</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Carrier Service Levels</td>
|
||||
<td>Sample 2</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Carrier Rules</td>
|
||||
<td>Sample 3</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Carrier Audit</td>
|
||||
<td>Sample 4</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
<x-layouts::app :title="__('Configuration')">
|
||||
<div class="container-fluid py-4 module-page module-configuration">
|
||||
<div class="container-fluid py-4 module-page module-configuration">
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="Configuration"
|
||||
<x-wms.page-hero eyebrow="Operations" title="Configuration"
|
||||
description="Centralise system settings, warehouse defaults, interface flags, and feature switches."
|
||||
icon="fa-solid fa-sliders"
|
||||
/>
|
||||
icon="fa-solid fa-sliders" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">System Settings</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for system settings.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">System Settings</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for system settings.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Feature Flags</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for feature flags.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Feature Flags</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for feature flags.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Warehouse Defaults</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for warehouse defaults.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Warehouse Defaults</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for warehouse defaults.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Interface Settings</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for interface settings.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Interface Settings</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for interface settings.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<section class="card border-0 shadow-sm">
|
||||
@@ -53,7 +50,8 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Configuration workbench</h2>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.</p>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers,
|
||||
queries, DataTables, or Livewire.</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
@@ -78,26 +76,38 @@
|
||||
<tr>
|
||||
<td>System Settings</td>
|
||||
<td>Sample 1</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Feature Flags</td>
|
||||
<td>Sample 2</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Warehouse Defaults</td>
|
||||
<td>Sample 3</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Interface Settings</td>
|
||||
<td>Sample 4</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,63 +1,91 @@
|
||||
@php
|
||||
$required = '<span class="text-red-600 dark:text-red-400">*</span>';
|
||||
$textInput = 'mt-2 min-h-11 w-full rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100';
|
||||
$textareaInput = 'mt-2 min-h-28 w-full rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100';
|
||||
$textInput =
|
||||
'mt-2 min-h-11 w-full rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100';
|
||||
$textareaInput =
|
||||
'mt-2 min-h-28 w-full rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100';
|
||||
$labelClass = 'text-sm font-semibold text-zinc-900 dark:text-zinc-100';
|
||||
@endphp
|
||||
|
||||
<div class="grid gap-5 md:grid-cols-3">
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="alpha2">Alpha-2 {!! $required !!}</label>
|
||||
<input id="alpha2" name="alpha2" maxlength="2" value="{{ old('alpha2', $country->alpha2) }}" class="{{ $textInput }} uppercase" placeholder="GB" />
|
||||
@error('alpha2')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="alpha2" name="alpha2" maxlength="2" value="{{ old('alpha2', $country->alpha2) }}"
|
||||
class="{{ $textInput }} uppercase" placeholder="GB" />
|
||||
@error('alpha2')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="alpha3">Alpha-3 {!! $required !!}</label>
|
||||
<input id="alpha3" name="alpha3" maxlength="3" value="{{ old('alpha3', $country->alpha3) }}" class="{{ $textInput }} uppercase" placeholder="GBR" />
|
||||
@error('alpha3')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="alpha3" name="alpha3" maxlength="3" value="{{ old('alpha3', $country->alpha3) }}"
|
||||
class="{{ $textInput }} uppercase" placeholder="GBR" />
|
||||
@error('alpha3')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="numeric">Numeric {!! $required !!}</label>
|
||||
<input id="numeric" name="numeric" maxlength="3" value="{{ old('numeric', $country->numeric) }}" class="{{ $textInput }}" placeholder="826" />
|
||||
@error('numeric')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="numeric" name="numeric" maxlength="3" value="{{ old('numeric', $country->numeric) }}"
|
||||
class="{{ $textInput }}" placeholder="826" />
|
||||
@error('numeric')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="name">Canonical ISO name {!! $required !!}</label>
|
||||
<input id="name" name="name" value="{{ old('name', $country->name) }}" class="{{ $textInput }}" />
|
||||
@error('name')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
@error('name')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="official_name">Official name</label>
|
||||
<input id="official_name" name="official_name" value="{{ old('official_name', $country->official_name) }}" class="{{ $textInput }}" />
|
||||
@error('official_name')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="official_name" name="official_name" value="{{ old('official_name', $country->official_name) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('official_name')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="common_name">Common name</label>
|
||||
<input id="common_name" name="common_name" value="{{ old('common_name', $country->common_name) }}" class="{{ $textInput }}" />
|
||||
@error('common_name')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="common_name" name="common_name" value="{{ old('common_name', $country->common_name) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('common_name')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="display_name">Display name override</label>
|
||||
<input id="display_name" name="display_name" value="{{ old('display_name', $country->display_name) }}" class="{{ $textInput }}" />
|
||||
@error('display_name')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="display_name" name="display_name" value="{{ old('display_name', $country->display_name) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('display_name')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="native_name">Native name</label>
|
||||
<input id="native_name" name="native_name" value="{{ old('native_name', $country->native_name) }}" class="{{ $textInput }}" />
|
||||
@error('native_name')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="native_name" name="native_name" value="{{ old('native_name', $country->native_name) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('native_name')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="capital">Capital</label>
|
||||
<input id="capital" name="capital" value="{{ old('capital', $country->capital) }}" class="{{ $textInput }}" />
|
||||
@error('capital')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="capital" name="capital" value="{{ old('capital', $country->capital) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('capital')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -68,40 +96,59 @@
|
||||
<label class="{{ $labelClass }}" for="iso_status">ISO lifecycle status {!! $required !!}</label>
|
||||
<select id="iso_status" name="iso_status" class="{{ $textInput }}">
|
||||
@foreach (\Modules\Countries\Support\Iso3166::statuses() as $status)
|
||||
<option value="{{ $status }}" @selected(old('iso_status', $country->iso_status ?? 'official') === $status)>{{ str($status)->replace('_', ' ')->title() }}</option>
|
||||
<option value="{{ $status }}" @selected(old('iso_status', $country->iso_status ?? 'official') === $status)>
|
||||
{{ str($status)->replace('_', ' ')->title() }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('iso_status')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
@error('iso_status')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="sovereignty_status">Sovereignty status</label>
|
||||
<input id="sovereignty_status" name="sovereignty_status" value="{{ old('sovereignty_status', $country->sovereignty_status) }}" class="{{ $textInput }}" />
|
||||
@error('sovereignty_status')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="sovereignty_status" name="sovereignty_status"
|
||||
value="{{ old('sovereignty_status', $country->sovereignty_status) }}" class="{{ $textInput }}" />
|
||||
@error('sovereignty_status')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="continent">Continent</label>
|
||||
<input id="continent" name="continent" value="{{ old('continent', $country->continent) }}" class="{{ $textInput }}" />
|
||||
@error('continent')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="continent" name="continent" value="{{ old('continent', $country->continent) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('continent')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="region">Region</label>
|
||||
<input id="region" name="region" value="{{ old('region', $country->region) }}" class="{{ $textInput }}" />
|
||||
@error('region')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="region" name="region" value="{{ old('region', $country->region) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('region')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="subregion">Subregion</label>
|
||||
<input id="subregion" name="subregion" value="{{ old('subregion', $country->subregion) }}" class="{{ $textInput }}" />
|
||||
@error('subregion')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="subregion" name="subregion" value="{{ old('subregion', $country->subregion) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('subregion')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="intermediate_region">Intermediate region</label>
|
||||
<input id="intermediate_region" name="intermediate_region" value="{{ old('intermediate_region', $country->intermediate_region) }}" class="{{ $textInput }}" />
|
||||
@error('intermediate_region')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="intermediate_region" name="intermediate_region"
|
||||
value="{{ old('intermediate_region', $country->intermediate_region) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('intermediate_region')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -111,110 +158,162 @@
|
||||
<div class="mt-4 grid gap-5 md:grid-cols-3">
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="flag_emoji">Flag emoji</label>
|
||||
<input id="flag_emoji" name="flag_emoji" value="{{ old('flag_emoji', $country->flag_emoji) }}" class="{{ $textInput }}" />
|
||||
@error('flag_emoji')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="flag_emoji" name="flag_emoji" value="{{ old('flag_emoji', $country->flag_emoji) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('flag_emoji')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="calling_code">Calling code</label>
|
||||
<input id="calling_code" name="calling_code" value="{{ old('calling_code', $country->calling_code) }}" class="{{ $textInput }}" placeholder="+44" />
|
||||
@error('calling_code')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="calling_code" name="calling_code" value="{{ old('calling_code', $country->calling_code) }}"
|
||||
class="{{ $textInput }}" placeholder="+44" />
|
||||
@error('calling_code')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="tld">Top-level domain</label>
|
||||
<input id="tld" name="tld" value="{{ old('tld', $country->tld) }}" class="{{ $textInput }}" placeholder=".uk" />
|
||||
@error('tld')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="tld" name="tld" value="{{ old('tld', $country->tld) }}"
|
||||
class="{{ $textInput }}" placeholder=".uk" />
|
||||
@error('tld')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="currency_code">Currency code</label>
|
||||
<input id="currency_code" name="currency_code" maxlength="3" value="{{ old('currency_code', $country->currency_code) }}" class="{{ $textInput }} uppercase" placeholder="GBP" />
|
||||
@error('currency_code')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="currency_code" name="currency_code" maxlength="3"
|
||||
value="{{ old('currency_code', $country->currency_code) }}" class="{{ $textInput }} uppercase"
|
||||
placeholder="GBP" />
|
||||
@error('currency_code')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="currency_name">Currency name</label>
|
||||
<input id="currency_name" name="currency_name" value="{{ old('currency_name', $country->currency_name) }}" class="{{ $textInput }}" />
|
||||
@error('currency_name')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="currency_name" name="currency_name"
|
||||
value="{{ old('currency_name', $country->currency_name) }}" class="{{ $textInput }}" />
|
||||
@error('currency_name')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="currency_symbol">Currency symbol</label>
|
||||
<input id="currency_symbol" name="currency_symbol" value="{{ old('currency_symbol', $country->currency_symbol) }}" class="{{ $textInput }}" />
|
||||
@error('currency_symbol')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="currency_symbol" name="currency_symbol"
|
||||
value="{{ old('currency_symbol', $country->currency_symbol) }}" class="{{ $textInput }}" />
|
||||
@error('currency_symbol')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="latitude">Latitude</label>
|
||||
<input id="latitude" name="latitude" type="number" step="0.0000001" value="{{ old('latitude', $country->latitude) }}" class="{{ $textInput }}" />
|
||||
@error('latitude')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="latitude" name="latitude" type="number" step="0.0000001"
|
||||
value="{{ old('latitude', $country->latitude) }}" class="{{ $textInput }}" />
|
||||
@error('latitude')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="longitude">Longitude</label>
|
||||
<input id="longitude" name="longitude" type="number" step="0.0000001" value="{{ old('longitude', $country->longitude) }}" class="{{ $textInput }}" />
|
||||
@error('longitude')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="longitude" name="longitude" type="number" step="0.0000001"
|
||||
value="{{ old('longitude', $country->longitude) }}" class="{{ $textInput }}" />
|
||||
@error('longitude')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="sort_order">Sort order</label>
|
||||
<input id="sort_order" name="sort_order" type="number" min="0" value="{{ old('sort_order', $country->sort_order ?? 0) }}" class="{{ $textInput }}" />
|
||||
@error('sort_order')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="sort_order" name="sort_order" type="number" min="0"
|
||||
value="{{ old('sort_order', $country->sort_order ?? 0) }}" class="{{ $textInput }}" />
|
||||
@error('sort_order')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="ioc_code">IOC code</label>
|
||||
<input id="ioc_code" name="ioc_code" value="{{ old('ioc_code', $country->ioc_code) }}" class="{{ $textInput }} uppercase" />
|
||||
@error('ioc_code')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="ioc_code" name="ioc_code" value="{{ old('ioc_code', $country->ioc_code) }}"
|
||||
class="{{ $textInput }} uppercase" />
|
||||
@error('ioc_code')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="fifa_code">FIFA code</label>
|
||||
<input id="fifa_code" name="fifa_code" value="{{ old('fifa_code', $country->fifa_code) }}" class="{{ $textInput }} uppercase" />
|
||||
@error('fifa_code')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="fifa_code" name="fifa_code" value="{{ old('fifa_code', $country->fifa_code) }}"
|
||||
class="{{ $textInput }} uppercase" />
|
||||
@error('fifa_code')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="postal_code_format">Postal-code format</label>
|
||||
<input id="postal_code_format" name="postal_code_format" value="{{ old('postal_code_format', $country->postal_code_format) }}" class="{{ $textInput }}" />
|
||||
@error('postal_code_format')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="postal_code_format" name="postal_code_format"
|
||||
value="{{ old('postal_code_format', $country->postal_code_format) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('postal_code_format')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-3">
|
||||
<label class="{{ $labelClass }}" for="flag_svg_url">Flag SVG URL</label>
|
||||
<input id="flag_svg_url" name="flag_svg_url" value="{{ old('flag_svg_url', $country->flag_svg_url) }}" class="{{ $textInput }}" />
|
||||
@error('flag_svg_url')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="flag_svg_url" name="flag_svg_url" value="{{ old('flag_svg_url', $country->flag_svg_url) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('flag_svg_url')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-3">
|
||||
<label class="{{ $labelClass }}" for="flag_png_url">Flag PNG URL</label>
|
||||
<input id="flag_png_url" name="flag_png_url" value="{{ old('flag_png_url', $country->flag_png_url) }}" class="{{ $textInput }}" />
|
||||
@error('flag_png_url')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="flag_png_url" name="flag_png_url" value="{{ old('flag_png_url', $country->flag_png_url) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('flag_png_url')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 border-t border-zinc-200 pt-6 dark:border-zinc-800">
|
||||
<h2 class="text-base font-semibold text-zinc-900 dark:text-zinc-100">JSON metadata</h2>
|
||||
<p class="mt-1 text-sm text-zinc-500 dark:text-zinc-400">Use valid JSON. Examples: languages as <code class="rounded bg-zinc-100 px-1 dark:bg-zinc-800">["en"]</code>, timezones as <code class="rounded bg-zinc-100 px-1 dark:bg-zinc-800">["Europe/London"]</code>.</p>
|
||||
<p class="mt-1 text-sm text-zinc-500 dark:text-zinc-400">Use valid JSON. Examples: languages as <code
|
||||
class="rounded bg-zinc-100 px-1 dark:bg-zinc-800">["en"]</code>, timezones as <code
|
||||
class="rounded bg-zinc-100 px-1 dark:bg-zinc-800">["Europe/London"]</code>.</p>
|
||||
<div class="mt-4 grid gap-5 md:grid-cols-3">
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="languages">Languages JSON</label>
|
||||
<textarea id="languages" name="languages" class="{{ $textareaInput }}">{{ old('languages', $country->languages ? json_encode($country->languages, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) : '') }}</textarea>
|
||||
@error('languages')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
@error('languages')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="timezones">Timezones JSON</label>
|
||||
<textarea id="timezones" name="timezones" class="{{ $textareaInput }}">{{ old('timezones', $country->timezones ? json_encode($country->timezones, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) : '') }}</textarea>
|
||||
@error('timezones')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
@error('timezones')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="metadata">Metadata JSON</label>
|
||||
<textarea id="metadata" name="metadata" class="{{ $textareaInput }}">{{ old('metadata', $country->metadata ? json_encode($country->metadata, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) : '') }}</textarea>
|
||||
@error('metadata')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
@error('metadata')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -222,46 +321,67 @@
|
||||
<div class="mt-8 border-t border-zinc-200 pt-6 dark:border-zinc-800">
|
||||
<h2 class="text-base font-semibold text-zinc-900 dark:text-zinc-100">Operational support</h2>
|
||||
<div class="mt-4 grid gap-5 md:grid-cols-3">
|
||||
<label class="flex items-center gap-3 rounded-2xl border border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<label
|
||||
class="flex items-center gap-3 rounded-2xl border border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<input type="hidden" name="is_active" value="0" />
|
||||
<input type="checkbox" name="is_active" value="1" @checked(old('is_active', $country->exists ? $country->is_active : true)) class="rounded border-zinc-300 text-blue-600 focus:ring-blue-500" />
|
||||
<span><span class="block text-sm font-semibold">Supported {!! $required !!}</span><span class="block text-xs text-zinc-500">Available in WMS selectors and workflows.</span></span>
|
||||
<input type="checkbox" name="is_active" value="1" @checked(old('is_active', $country->exists ? $country->is_active : true))
|
||||
class="rounded border-zinc-300 text-blue-600 focus:ring-blue-500" />
|
||||
<span><span class="block text-sm font-semibold">Supported {!! $required !!}</span><span
|
||||
class="block text-xs text-zinc-500">Available in WMS selectors and workflows.</span></span>
|
||||
</label>
|
||||
|
||||
<label class="flex items-center gap-3 rounded-2xl border border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<label
|
||||
class="flex items-center gap-3 rounded-2xl border border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<input type="hidden" name="independent" value="0" />
|
||||
<input type="checkbox" name="independent" value="1" @checked(old('independent', $country->independent)) class="rounded border-zinc-300 text-blue-600 focus:ring-blue-500" />
|
||||
<span><span class="block text-sm font-semibold">Independent state</span><span class="block text-xs text-zinc-500">Used for reporting and compliance filters.</span></span>
|
||||
<input type="checkbox" name="independent" value="1" @checked(old('independent', $country->independent))
|
||||
class="rounded border-zinc-300 text-blue-600 focus:ring-blue-500" />
|
||||
<span><span class="block text-sm font-semibold">Independent state</span><span
|
||||
class="block text-xs text-zinc-500">Used for reporting and compliance filters.</span></span>
|
||||
</label>
|
||||
|
||||
<label class="flex items-center gap-3 rounded-2xl border border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<label
|
||||
class="flex items-center gap-3 rounded-2xl border border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<input type="hidden" name="un_member" value="0" />
|
||||
<input type="checkbox" name="un_member" value="1" @checked(old('un_member', $country->un_member)) class="rounded border-zinc-300 text-blue-600 focus:ring-blue-500" />
|
||||
<span><span class="block text-sm font-semibold">UN member</span><span class="block text-xs text-zinc-500">Optional global reference attribute.</span></span>
|
||||
<input type="checkbox" name="un_member" value="1" @checked(old('un_member', $country->un_member))
|
||||
class="rounded border-zinc-300 text-blue-600 focus:ring-blue-500" />
|
||||
<span><span class="block text-sm font-semibold">UN member</span><span
|
||||
class="block text-xs text-zinc-500">Optional global reference attribute.</span></span>
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="source">Source</label>
|
||||
<input id="source" name="source" value="{{ old('source', $country->source) }}" class="{{ $textInput }}" />
|
||||
@error('source')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="source" name="source" value="{{ old('source', $country->source) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('source')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="source_version">Source version</label>
|
||||
<input id="source_version" name="source_version" value="{{ old('source_version', $country->source_version) }}" class="{{ $textInput }}" />
|
||||
@error('source_version')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="source_version" name="source_version"
|
||||
value="{{ old('source_version', $country->source_version) }}" class="{{ $textInput }}" />
|
||||
@error('source_version')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="{{ $labelClass }}" for="last_synced_at">Last synced at</label>
|
||||
<input id="last_synced_at" name="last_synced_at" type="datetime-local" value="{{ old('last_synced_at', $country->last_synced_at?->format('Y-m-d\\TH:i')) }}" class="{{ $textInput }}" />
|
||||
@error('last_synced_at')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<input id="last_synced_at" name="last_synced_at" type="datetime-local"
|
||||
value="{{ old('last_synced_at', $country->last_synced_at?->format('Y-m-d\\TH:i')) }}"
|
||||
class="{{ $textInput }}" />
|
||||
@error('last_synced_at')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-3">
|
||||
<label class="{{ $labelClass }}" for="address_format">Address format</label>
|
||||
<textarea id="address_format" name="address_format" class="{{ $textareaInput }}">{{ old('address_format', $country->address_format) }}</textarea>
|
||||
@error('address_format')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
@error('address_format')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,29 +1,40 @@
|
||||
<x-layouts.app>
|
||||
<div class="mx-auto max-w-[1400px] space-y-6" dusk="countries-create">
|
||||
<div class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="bg-gradient-to-br from-slate-950 via-slate-900 to-blue-950 px-6 py-7 text-white">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">ISO 3166 country support</p>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">ISO 3166 country
|
||||
support</p>
|
||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight">Create country</h1>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">Create a governed country reference record for platform, address, billing, carrier, customs and integration workflows.</p>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">Create a governed country reference record for
|
||||
platform, address, billing, carrier, customs and integration workflows.</p>
|
||||
</div>
|
||||
<a href="{{ route('countries.index') }}" wire:navigate class="rounded-xl bg-white/10 px-4 py-2.5 text-sm font-semibold text-white ring-1 ring-white/20 hover:bg-white/20">Back to countries</a>
|
||||
<a href="{{ route('countries.index') }}" wire:navigate
|
||||
class="rounded-xl bg-white/10 px-4 py-2.5 text-sm font-semibold text-white ring-1 ring-white/20 hover:bg-white/20">Back
|
||||
to countries</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('countries.store') }}" class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<form method="POST" action="{{ route('countries.store') }}"
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
@csrf
|
||||
<div class="mb-5 rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800 dark:border-red-900 dark:bg-red-950 dark:text-red-200">
|
||||
Fields marked with <span class="font-bold text-red-600 dark:text-red-300">*</span> are mandatory ISO/WMS support fields.
|
||||
<div
|
||||
class="mb-5 rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800 dark:border-red-900 dark:bg-red-950 dark:text-red-200">
|
||||
Fields marked with <span class="font-bold text-red-600 dark:text-red-300">*</span> are mandatory ISO/WMS
|
||||
support fields.
|
||||
</div>
|
||||
|
||||
@include('countries::_form', ['country' => $country])
|
||||
|
||||
<div class="mt-8 flex flex-wrap gap-2 border-t border-zinc-200 pt-6 dark:border-zinc-800">
|
||||
<button type="submit" class="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">Create country</button>
|
||||
<a href="{{ route('countries.index') }}" wire:navigate class="rounded-xl bg-zinc-100 px-4 py-2.5 text-sm font-semibold text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-100 dark:hover:bg-zinc-700">Cancel</a>
|
||||
<button type="submit"
|
||||
class="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">Create
|
||||
country</button>
|
||||
<a href="{{ route('countries.index') }}" wire:navigate
|
||||
class="rounded-xl bg-zinc-100 px-4 py-2.5 text-sm font-semibold text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-100 dark:hover:bg-zinc-700">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,33 +1,46 @@
|
||||
<x-layouts.app>
|
||||
<div class="mx-auto max-w-[1400px] space-y-6" dusk="countries-edit">
|
||||
<div class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="bg-gradient-to-br from-slate-950 via-slate-900 to-blue-950 px-6 py-7 text-white">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">ISO 3166 country support</p>
|
||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight">Edit {{ $country->flag_emoji }} {{ $country->effective_name }}</h1>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">Expose and manage every ISO, operational, regional, currency, logistics and WMS support parameter.</p>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">ISO 3166 country
|
||||
support</p>
|
||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight">Edit {{ $country->flag_emoji }}
|
||||
{{ $country->effective_name }}</h1>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">Expose and manage every ISO, operational,
|
||||
regional, currency, logistics and WMS support parameter.</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a href="{{ route('countries.show', $country) }}" wire:navigate class="rounded-xl bg-white/10 px-4 py-2.5 text-sm font-semibold text-white ring-1 ring-white/20 hover:bg-white/20">View country</a>
|
||||
<a href="{{ route('countries.index') }}" wire:navigate class="rounded-xl bg-white/10 px-4 py-2.5 text-sm font-semibold text-white ring-1 ring-white/20 hover:bg-white/20">Back</a>
|
||||
<a href="{{ route('countries.show', $country) }}" wire:navigate
|
||||
class="rounded-xl bg-white/10 px-4 py-2.5 text-sm font-semibold text-white ring-1 ring-white/20 hover:bg-white/20">View
|
||||
country</a>
|
||||
<a href="{{ route('countries.index') }}" wire:navigate
|
||||
class="rounded-xl bg-white/10 px-4 py-2.5 text-sm font-semibold text-white ring-1 ring-white/20 hover:bg-white/20">Back</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('countries.update', $country) }}" class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<form method="POST" action="{{ route('countries.update', $country) }}"
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="mb-5 rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800 dark:border-red-900 dark:bg-red-950 dark:text-red-200">
|
||||
Fields marked with <span class="font-bold text-red-600 dark:text-red-300">*</span> are mandatory ISO/WMS support fields.
|
||||
<div
|
||||
class="mb-5 rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800 dark:border-red-900 dark:bg-red-950 dark:text-red-200">
|
||||
Fields marked with <span class="font-bold text-red-600 dark:text-red-300">*</span> are mandatory ISO/WMS
|
||||
support fields.
|
||||
</div>
|
||||
|
||||
@include('countries::_form', ['country' => $country])
|
||||
|
||||
<div class="mt-8 flex flex-wrap gap-2 border-t border-zinc-200 pt-6 dark:border-zinc-800">
|
||||
<button type="submit" class="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">Save country</button>
|
||||
<a href="{{ route('countries.show', $country) }}" wire:navigate class="rounded-xl bg-zinc-100 px-4 py-2.5 text-sm font-semibold text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-100 dark:hover:bg-zinc-700">Cancel</a>
|
||||
<button type="submit"
|
||||
class="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">Save
|
||||
country</button>
|
||||
<a href="{{ route('countries.show', $country) }}" wire:navigate
|
||||
class="rounded-xl bg-zinc-100 px-4 py-2.5 text-sm font-semibold text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-100 dark:hover:bg-zinc-700">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,61 +1,96 @@
|
||||
<x-layouts.app>
|
||||
<div class="mx-auto max-w-[1800px] space-y-6" dusk="countries-index">
|
||||
<div class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="bg-gradient-to-br from-slate-950 via-slate-900 to-blue-950 px-6 py-7 text-white">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">Reference data</p>
|
||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight">Countries</h1>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">Governed ISO 3166 country records for tenants, companies, warehouses, billing, shipping, carriers, customs and integrations.</p>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">Governed ISO 3166 country records for tenants,
|
||||
companies, warehouses, billing, shipping, carriers, customs and integrations.</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<form method="POST" action="{{ route('countries.sync') }}">
|
||||
@csrf
|
||||
<button type="submit" class="rounded-xl bg-white/10 px-4 py-2.5 text-sm font-semibold text-white ring-1 ring-white/20 hover:bg-white/20">Sync ISO data</button>
|
||||
<button type="submit"
|
||||
class="rounded-xl bg-white/10 px-4 py-2.5 text-sm font-semibold text-white ring-1 ring-white/20 hover:bg-white/20">Sync
|
||||
ISO data</button>
|
||||
</form>
|
||||
<a href="{{ route('countries.create') }}" wire:navigate class="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">New country</a>
|
||||
<a href="{{ route('countries.create') }}" wire:navigate
|
||||
class="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">New
|
||||
country</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="rounded-2xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm font-medium text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-200">{{ session('success') }}</div>
|
||||
<div
|
||||
class="rounded-2xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm font-medium text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-200">
|
||||
{{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900"><div class="text-sm text-zinc-500">Total countries</div><div class="mt-2 text-3xl font-semibold">{{ number_format($totalCount) }}</div></div>
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900"><div class="text-sm text-zinc-500">Active</div><div class="mt-2 text-3xl font-semibold text-emerald-700 dark:text-emerald-300">{{ number_format($activeCount) }}</div></div>
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900"><div class="text-sm text-zinc-500">Inactive</div><div class="mt-2 text-3xl font-semibold text-amber-700 dark:text-amber-300">{{ number_format($inactiveCount) }}</div></div>
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="text-sm text-zinc-500">Total countries</div>
|
||||
<div class="mt-2 text-3xl font-semibold">{{ number_format($totalCount) }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="text-sm text-zinc-500">Active</div>
|
||||
<div class="mt-2 text-3xl font-semibold text-emerald-700 dark:text-emerald-300">
|
||||
{{ number_format($activeCount) }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="text-sm text-zinc-500">Inactive</div>
|
||||
<div class="mt-2 text-3xl font-semibold text-amber-700 dark:text-amber-300">
|
||||
{{ number_format($inactiveCount) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="GET" action="{{ route('countries.index') }}" class="grid gap-3 rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-700 dark:bg-zinc-900 md:grid-cols-[1fr_220px_220px_auto] md:items-center">
|
||||
<input name="search" value="{{ $search }}" placeholder="Search by country, ISO code, currency or calling code" class="min-h-11 rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100" />
|
||||
<select name="status" class="min-h-11 rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100">
|
||||
<form method="GET" action="{{ route('countries.index') }}"
|
||||
class="grid gap-3 rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-700 dark:bg-zinc-900 md:grid-cols-[1fr_220px_220px_auto] md:items-center">
|
||||
<input name="search" value="{{ $search }}"
|
||||
placeholder="Search by country, ISO code, currency or calling code"
|
||||
class="min-h-11 rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100" />
|
||||
<select name="status"
|
||||
class="min-h-11 rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100">
|
||||
<option value="all" @selected($status === 'all')>All statuses</option>
|
||||
<option value="active" @selected($status === 'active')>Active</option>
|
||||
<option value="inactive" @selected($status === 'inactive')>Inactive</option>
|
||||
</select>
|
||||
<select name="region" class="min-h-11 rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100">
|
||||
<select name="region"
|
||||
class="min-h-11 rounded-xl border-zinc-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100">
|
||||
<option value="all" @selected($region === 'all')>All regions</option>
|
||||
@foreach ($regions as $availableRegion)
|
||||
<option value="{{ $availableRegion }}" @selected($region === $availableRegion)>{{ $availableRegion }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<button type="submit" class="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">Filter</button>
|
||||
<button type="submit"
|
||||
class="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">Filter</button>
|
||||
</form>
|
||||
|
||||
<div class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-zinc-200 dark:divide-zinc-800">
|
||||
<thead class="bg-zinc-50 dark:bg-zinc-950">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Country</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">ISO</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Region</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Currency</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Support</th>
|
||||
<th class="px-4 py-3 text-right text-xs font-bold uppercase tracking-wide text-zinc-500">Actions</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">
|
||||
Country</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">ISO
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">
|
||||
Region</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">
|
||||
Currency</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">
|
||||
Support</th>
|
||||
<th class="px-4 py-3 text-right text-xs font-bold uppercase tracking-wide text-zinc-500">
|
||||
Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-zinc-100 dark:divide-zinc-800">
|
||||
@@ -65,19 +100,48 @@
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-xl">{{ $country->flag_emoji }}</span>
|
||||
<div>
|
||||
<div class="font-semibold text-zinc-900 dark:text-zinc-100">{{ $country->effective_name }}</div>
|
||||
<div class="text-xs text-zinc-500">{{ $country->official_name ?: $country->name }}</div>
|
||||
<div class="font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{{ $country->effective_name }}</div>
|
||||
<div class="text-xs text-zinc-500">
|
||||
{{ $country->official_name ?: $country->name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-3"><div class="font-mono text-sm text-blue-700 dark:text-blue-300">{{ $country->alpha2 }} / {{ $country->alpha3 }} / {{ $country->numeric }}</div><div class="text-xs text-zinc-500">{{ str($country->iso_status ?? 'official')->replace('_', ' ')->title() }}</div></td>
|
||||
<td class="px-4 py-3 text-sm text-zinc-600 dark:text-zinc-300"><div>{{ $country->region ?: '—' }}</div><div class="text-xs text-zinc-500">{{ $country->subregion ?: '—' }}</div></td>
|
||||
<td class="px-4 py-3 text-sm"><div class="font-mono text-zinc-900 dark:text-zinc-100">{{ $country->currency_code ?: '—' }}</div><div class="text-xs text-zinc-500">{{ $country->currency_name ?: '—' }}</div></td>
|
||||
<td class="px-4 py-3">@if ($country->is_active)<span class="rounded-full bg-emerald-100 px-2.5 py-1 text-xs font-bold text-emerald-800 dark:bg-emerald-950 dark:text-emerald-200">Active</span>@else<span class="rounded-full bg-amber-100 px-2.5 py-1 text-xs font-bold text-amber-800 dark:bg-amber-950 dark:text-amber-200">Inactive</span>@endif</td>
|
||||
<td class="px-4 py-3 text-right"><a href="{{ route('countries.show', $country) }}" wire:navigate class="rounded-lg bg-blue-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-blue-700">Open</a> <a href="{{ route('countries.edit', $country) }}" wire:navigate class="rounded-lg bg-emerald-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-emerald-700">Edit</a></td>
|
||||
<td class="px-4 py-3">
|
||||
<div class="font-mono text-sm text-blue-700 dark:text-blue-300">
|
||||
{{ $country->alpha2 }} / {{ $country->alpha3 }} / {{ $country->numeric }}
|
||||
</div>
|
||||
<div class="text-xs text-zinc-500">
|
||||
{{ str($country->iso_status ?? 'official')->replace('_', ' ')->title() }}</div>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
<div>{{ $country->region ?: '—' }}</div>
|
||||
<div class="text-xs text-zinc-500">{{ $country->subregion ?: '—' }}</div>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm">
|
||||
<div class="font-mono text-zinc-900 dark:text-zinc-100">
|
||||
{{ $country->currency_code ?: '—' }}</div>
|
||||
<div class="text-xs text-zinc-500">{{ $country->currency_name ?: '—' }}</div>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
@if ($country->is_active)
|
||||
<span
|
||||
class="rounded-full bg-emerald-100 px-2.5 py-1 text-xs font-bold text-emerald-800 dark:bg-emerald-950 dark:text-emerald-200">Active</span>@else<span
|
||||
class="rounded-full bg-amber-100 px-2.5 py-1 text-xs font-bold text-amber-800 dark:bg-amber-950 dark:text-amber-200">Inactive</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right"><a href="{{ route('countries.show', $country) }}"
|
||||
wire:navigate
|
||||
class="rounded-lg bg-blue-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-blue-700">Open</a>
|
||||
<a href="{{ route('countries.edit', $country) }}" wire:navigate
|
||||
class="rounded-lg bg-emerald-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-emerald-700">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="px-4 py-10 text-center text-sm text-zinc-500">No countries matched your search.</td></tr>
|
||||
<tr>
|
||||
<td colspan="6" class="px-4 py-10 text-center text-sm text-zinc-500">No countries
|
||||
matched your search.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Module Countries</title>
|
||||
|
||||
{{-- Laravel Mix - CSS File --}}
|
||||
{{-- <link rel="stylesheet" href="{{ mix('css/countries.css') }}"> --}}
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Module Countries</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);" aria-hidden="true"></div>
|
||||
@yield('content')
|
||||
{{-- Laravel Mix - CSS File --}}
|
||||
{{-- <link rel="stylesheet" href="{{ mix('css/countries.css') }}"> --}}
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
|
||||
aria-hidden="true"></div>
|
||||
@yield('content')
|
||||
|
||||
{{-- Laravel Mix - JS File --}}
|
||||
{{-- <script src="{{ mix('js/countries.js') }}"></script> --}}
|
||||
</body>
|
||||
|
||||
{{-- Laravel Mix - JS File --}}
|
||||
{{-- <script src="{{ mix('js/countries.js') }}"></script> --}}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
<x-layouts.app>
|
||||
<div class="mx-auto max-w-[1400px] space-y-6" dusk="countries-show">
|
||||
<div class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="bg-gradient-to-br from-slate-950 via-slate-900 to-blue-950 px-6 py-7 text-white">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">ISO 3166 country</p>
|
||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight">{{ $country->flag_emoji }} {{ $country->effective_name }}</h1>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">{{ $country->official_name ?: $country->name }}</p>
|
||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight">{{ $country->flag_emoji }}
|
||||
{{ $country->effective_name }}</h1>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">
|
||||
{{ $country->official_name ?: $country->name }}</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a href="{{ route('countries.edit', $country) }}" wire:navigate class="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-blue-700">Edit country</a>
|
||||
<a href="{{ route('countries.index') }}" wire:navigate class="rounded-xl bg-white/10 px-4 py-2.5 text-sm font-semibold text-white ring-1 ring-white/20 hover:bg-white/20">Back</a>
|
||||
<a href="{{ route('countries.edit', $country) }}" wire:navigate
|
||||
class="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-blue-700">Edit
|
||||
country</a>
|
||||
<a href="{{ route('countries.index') }}" wire:navigate
|
||||
class="rounded-xl bg-white/10 px-4 py-2.5 text-sm font-semibold text-white ring-1 ring-white/20 hover:bg-white/20">Back</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="rounded-2xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm font-medium text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-200">{{ session('success') }}</div>
|
||||
<div
|
||||
class="rounded-2xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm font-medium text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-200">
|
||||
{{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
@php
|
||||
@@ -31,7 +39,9 @@
|
||||
'Display override' => $country->display_name ?: '—',
|
||||
'Native name' => $country->native_name ?: '—',
|
||||
'Capital' => $country->capital ?: '—',
|
||||
'ISO status' => str($country->iso_status ?? 'official')->replace('_', ' ')->title(),
|
||||
'ISO status' => str($country->iso_status ?? 'official')
|
||||
->replace('_', ' ')
|
||||
->title(),
|
||||
'Sovereignty status' => $country->sovereignty_status ?: '—',
|
||||
'Independent' => is_null($country->independent) ? '—' : ($country->independent ? 'Yes' : 'No'),
|
||||
'UN member' => is_null($country->un_member) ? '—' : ($country->un_member ? 'Yes' : 'No'),
|
||||
@@ -59,16 +69,19 @@
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
@foreach ($fields as $label => $value)
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="text-xs font-bold uppercase tracking-wide text-zinc-500">{{ $label }}</div>
|
||||
<div class="mt-2 break-words text-base font-semibold text-zinc-900 dark:text-zinc-100">{{ $value }}</div>
|
||||
<div class="mt-2 break-words text-base font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{{ $value }}</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 lg:grid-cols-3">
|
||||
@foreach (['Languages' => $country->languages, 'Timezones' => $country->timezones, 'Metadata' => $country->metadata] as $label => $value)
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<h2 class="text-sm font-semibold text-zinc-900 dark:text-zinc-100">{{ $label }}</h2>
|
||||
<pre class="mt-3 max-h-80 overflow-auto rounded-xl bg-zinc-950 p-4 text-xs text-zinc-100">{{ $value ? json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) : '—' }}</pre>
|
||||
</div>
|
||||
@@ -77,11 +90,14 @@
|
||||
|
||||
@if ($country->address_format || $country->flag_svg_url || $country->flag_png_url)
|
||||
<div class="grid gap-4 lg:grid-cols-3">
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900 lg:col-span-2">
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900 lg:col-span-2">
|
||||
<h2 class="text-sm font-semibold text-zinc-900 dark:text-zinc-100">Address format</h2>
|
||||
<pre class="mt-3 whitespace-pre-wrap rounded-xl bg-zinc-100 p-4 text-sm text-zinc-700 dark:bg-zinc-950 dark:text-zinc-200">{{ $country->address_format ?: '—' }}</pre>
|
||||
<pre
|
||||
class="mt-3 whitespace-pre-wrap rounded-xl bg-zinc-100 p-4 text-sm text-zinc-700 dark:bg-zinc-950 dark:text-zinc-200">{{ $country->address_format ?: '—' }}</pre>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<h2 class="text-sm font-semibold text-zinc-900 dark:text-zinc-100">Flag assets</h2>
|
||||
<div class="mt-3 space-y-2 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
<div>SVG: {{ $country->flag_svg_url ?: '—' }}</div>
|
||||
@@ -92,38 +108,51 @@
|
||||
@endif
|
||||
|
||||
|
||||
<div class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="border-b border-zinc-200 px-5 py-4 dark:border-zinc-800">
|
||||
<h2 class="text-sm font-semibold text-zinc-900 dark:text-zinc-100">WMS usage report</h2>
|
||||
<p class="text-xs text-zinc-500">Best-effort counts across known WMS tables. Missing optional tables are skipped safely.</p>
|
||||
<p class="text-xs text-zinc-500">Best-effort counts across known WMS tables. Missing optional tables are
|
||||
skipped safely.</p>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-zinc-200 dark:divide-zinc-800">
|
||||
<thead class="bg-zinc-50 dark:bg-zinc-950">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Area</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Table</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Column</th>
|
||||
<th class="px-4 py-3 text-right text-xs font-bold uppercase tracking-wide text-zinc-500">References</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Area
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">
|
||||
Table</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">
|
||||
Column</th>
|
||||
<th class="px-4 py-3 text-right text-xs font-bold uppercase tracking-wide text-zinc-500">
|
||||
References</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-zinc-100 dark:divide-zinc-800">
|
||||
@forelse ($usageRows ?? [] as $row)
|
||||
<tr>
|
||||
<td class="px-4 py-3 text-sm font-semibold text-zinc-900 dark:text-zinc-100">{{ $row['label'] }}</td>
|
||||
<td class="px-4 py-3 text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{{ $row['label'] }}</td>
|
||||
<td class="px-4 py-3 font-mono text-xs text-zinc-500">{{ $row['table'] }}</td>
|
||||
<td class="px-4 py-3 font-mono text-xs text-blue-700 dark:text-blue-300">{{ $row['column'] }}</td>
|
||||
<td class="px-4 py-3 text-right text-sm font-semibold text-zinc-900 dark:text-zinc-100">{{ number_format($row['count']) }}</td>
|
||||
<td class="px-4 py-3 font-mono text-xs text-blue-700 dark:text-blue-300">
|
||||
{{ $row['column'] }}</td>
|
||||
<td class="px-4 py-3 text-right text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{{ number_format($row['count']) }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="4" class="px-4 py-8 text-center text-sm text-zinc-500">No country-aware WMS usage tables were detected yet.</td></tr>
|
||||
<tr>
|
||||
<td colspan="4" class="px-4 py-8 text-center text-sm text-zinc-500">No country-aware
|
||||
WMS usage tables were detected yet.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="border-b border-zinc-200 px-5 py-4 dark:border-zinc-800">
|
||||
<h2 class="text-sm font-semibold text-zinc-900 dark:text-zinc-100">ISO 3166-2 subdivisions</h2>
|
||||
<p class="text-xs text-zinc-500">Subdivisions support address, compliance and shipping workflows.</p>
|
||||
@@ -132,37 +161,51 @@
|
||||
<table class="min-w-full divide-y divide-zinc-200 dark:divide-zinc-800">
|
||||
<thead class="bg-zinc-50 dark:bg-zinc-950">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Code</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Name</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Type</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Status</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Code
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Name
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">Type
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-bold uppercase tracking-wide text-zinc-500">
|
||||
Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-zinc-100 dark:divide-zinc-800">
|
||||
@forelse ($country->subdivisions as $subdivision)
|
||||
<tr>
|
||||
<td class="px-4 py-3 font-mono text-sm text-blue-700 dark:text-blue-300">{{ $subdivision->code }}</td>
|
||||
<td class="px-4 py-3 text-sm font-semibold text-zinc-900 dark:text-zinc-100">{{ $subdivision->name }}</td>
|
||||
<td class="px-4 py-3 font-mono text-sm text-blue-700 dark:text-blue-300">
|
||||
{{ $subdivision->code }}</td>
|
||||
<td class="px-4 py-3 text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
{{ $subdivision->name }}</td>
|
||||
<td class="px-4 py-3 text-sm text-zinc-500">{{ $subdivision->type ?: '—' }}</td>
|
||||
<td class="px-4 py-3 text-sm text-zinc-500">{{ str($subdivision->iso_status)->replace('_', ' ')->title() }}</td>
|
||||
<td class="px-4 py-3 text-sm text-zinc-500">
|
||||
{{ str($subdivision->iso_status)->replace('_', ' ')->title() }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="4" class="px-4 py-8 text-center text-sm text-zinc-500">No subdivisions have been imported for this country yet.</td></tr>
|
||||
<tr>
|
||||
<td colspan="4" class="px-4 py-8 text-center text-sm text-zinc-500">No subdivisions
|
||||
have been imported for this country yet.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('countries.destroy', $country) }}" class="rounded-2xl border border-red-200 bg-red-50 p-5 dark:border-red-900 dark:bg-red-950">
|
||||
<form method="POST" action="{{ route('countries.destroy', $country) }}"
|
||||
class="rounded-2xl border border-red-200 bg-red-50 p-5 dark:border-red-900 dark:bg-red-950">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<div class="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold text-red-900 dark:text-red-100">Archive country</h2>
|
||||
<p class="text-sm text-red-700 dark:text-red-200">Soft-deletes the record for administration. Do not archive countries referenced by active business data.</p>
|
||||
<p class="text-sm text-red-700 dark:text-red-200">Soft-deletes the record for administration. Do not
|
||||
archive countries referenced by active business data.</p>
|
||||
</div>
|
||||
<button type="submit" class="rounded-xl bg-red-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-red-700" onclick="return confirm('Archive this country?')">Archive</button>
|
||||
<button type="submit"
|
||||
class="rounded-xl bg-red-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-red-700"
|
||||
onclick="return confirm('Archive this country?')">Archive</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
<x-layouts::app :title="__('Document Archive')">
|
||||
<div class="container-fluid py-4 module-page module-document-archive">
|
||||
<div class="container-fluid py-4 module-page module-document-archive">
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="Document Archive"
|
||||
<x-wms.page-hero eyebrow="Operations" title="Document Archive"
|
||||
description="Search cached Sentinel documents, shipment PDFs, labels, and BOL records."
|
||||
icon="fa-solid fa-file-lines"
|
||||
/>
|
||||
icon="fa-solid fa-file-lines" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Sentinel Doc Archive</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for sentinel doc archive.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Sentinel Doc Archive</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for sentinel doc archive.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Shipment PDFs</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for shipment pdfs.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Shipment PDFs</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for shipment pdfs.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Bill of Lading</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for bill of lading.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Bill of Lading</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for bill of lading.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Document Search</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for document search.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Document Search</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for document search.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<section class="card border-0 shadow-sm">
|
||||
@@ -53,7 +50,8 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Document Archive workbench</h2>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.</p>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers,
|
||||
queries, DataTables, or Livewire.</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
@@ -78,26 +76,38 @@
|
||||
<tr>
|
||||
<td>Sentinel Doc Archive</td>
|
||||
<td>Sample 1</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Shipment PDFs</td>
|
||||
<td>Sample 2</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bill of Lading</td>
|
||||
<td>Sample 3</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Document Search</td>
|
||||
<td>Sample 4</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
|
||||
@section('content')
|
||||
<div class="doc-page">
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => 'Create category',
|
||||
'subtitle' => 'Add a professional grouping for documents and operational knowledge.',
|
||||
])
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header"><strong>Category details</strong></div>
|
||||
<div class="doc-card-body">
|
||||
<form method="POST" action="{{ route('documentation.categories.store') }}">
|
||||
@include('documentation::partials.category-form')
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => 'Create category',
|
||||
'subtitle' => 'Add a professional grouping for documents and operational knowledge.',
|
||||
])
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header"><strong>Category details</strong></div>
|
||||
<div class="doc-card-body">
|
||||
<form method="POST" action="{{ route('documentation.categories.store') }}">
|
||||
@include('documentation::partials.category-form')
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
|
||||
@section('content')
|
||||
<div class="doc-page">
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => 'Edit category',
|
||||
'subtitle' => 'Update category naming, ordering, visibility, and presentation metadata.',
|
||||
])
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header"><strong>{{ $category->name }}</strong></div>
|
||||
<div class="doc-card-body">
|
||||
<form method="POST" action="{{ route('documentation.categories.update', $category) }}">
|
||||
@include('documentation::partials.category-form')
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => 'Edit category',
|
||||
'subtitle' => 'Update category naming, ordering, visibility, and presentation metadata.',
|
||||
])
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header"><strong>{{ $category->name }}</strong></div>
|
||||
<div class="doc-card-body">
|
||||
<form method="POST" action="{{ route('documentation.categories.update', $category) }}">
|
||||
@include('documentation::partials.category-form')
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -4,56 +4,68 @@
|
||||
|
||||
@section('content')
|
||||
<div class="doc-page">
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => 'Document categories',
|
||||
'subtitle' => 'Organise documents by policy, process, onboarding, operational support, or technical area.',
|
||||
])
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => 'Document categories',
|
||||
'subtitle' =>
|
||||
'Organise documents by policy, process, onboarding, operational support, or technical area.',
|
||||
])
|
||||
|
||||
@if(session('status'))
|
||||
<div class="doc-alert">{{ session('status') }}</div>
|
||||
@endif
|
||||
@if (session('status'))
|
||||
<div class="doc-alert">{{ session('status') }}</div>
|
||||
@endif
|
||||
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header">
|
||||
<strong>Categories</strong>
|
||||
<a class="doc-button category" href="{{ route('documentation.categories.create') }}">New category</a>
|
||||
</div>
|
||||
<div class="doc-card-body">
|
||||
<table class="doc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Documents</th>
|
||||
<th>Order</th>
|
||||
<th>Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($categories as $category)
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header">
|
||||
<strong>Categories</strong>
|
||||
<a class="doc-button category" href="{{ route('documentation.categories.create') }}">New category</a>
|
||||
</div>
|
||||
<div class="doc-card-body">
|
||||
<table class="doc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><strong>{{ $category->name }}</strong><div class="doc-muted">{{ $category->slug }}</div></td>
|
||||
<td>{{ $category->description ?: 'No description' }}</td>
|
||||
<td>{{ $category->documents_count }}</td>
|
||||
<td>{{ $category->sort_order }}</td>
|
||||
<td><span class="doc-badge {{ $category->is_active ? '' : 'archived' }}">{{ $category->is_active ? 'Active' : 'Inactive' }}</span></td>
|
||||
<td>
|
||||
<a class="doc-link" href="{{ route('documentation.categories.edit', $category) }}">Edit</a>
|
||||
<form method="POST" action="{{ route('documentation.categories.destroy', $category) }}" style="display:inline;" onsubmit="return confirm('Delete this category? Documents will become uncategorised.')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="doc-button danger" style="border:0;cursor:pointer;margin-left:10px;">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Documents</th>
|
||||
<th>Order</th>
|
||||
<th>Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="doc-muted">No categories found.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="margin-top:18px;">{{ $categories->links() }}</div>
|
||||
</div>
|
||||
</section>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($categories as $category)
|
||||
<tr>
|
||||
<td><strong>{{ $category->name }}</strong>
|
||||
<div class="doc-muted">{{ $category->slug }}</div>
|
||||
</td>
|
||||
<td>{{ $category->description ?: 'No description' }}</td>
|
||||
<td>{{ $category->documents_count }}</td>
|
||||
<td>{{ $category->sort_order }}</td>
|
||||
<td><span
|
||||
class="doc-badge {{ $category->is_active ? '' : 'archived' }}">{{ $category->is_active ? 'Active' : 'Inactive' }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<a class="doc-link"
|
||||
href="{{ route('documentation.categories.edit', $category) }}">Edit</a>
|
||||
<form method="POST"
|
||||
action="{{ route('documentation.categories.destroy', $category) }}"
|
||||
style="display:inline;"
|
||||
onsubmit="return confirm('Delete this category? Documents will become uncategorised.')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="doc-button danger"
|
||||
style="border:0;cursor:pointer;margin-left:10px;">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="doc-muted">No categories found.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="margin-top:18px;">{{ $categories->links() }}</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
|
||||
@section('content')
|
||||
<div class="doc-page">
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => 'Create document',
|
||||
'subtitle' => 'Add a controlled document, guide, policy, SOP, upload, or operational note.',
|
||||
])
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header"><strong>Document details</strong></div>
|
||||
<div class="doc-card-body">
|
||||
<form method="POST" action="{{ route('documentation.documents.store') }}" enctype="multipart/form-data">
|
||||
@include('documentation::partials.document-form')
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => 'Create document',
|
||||
'subtitle' => 'Add a controlled document, guide, policy, SOP, upload, or operational note.',
|
||||
])
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header"><strong>Document details</strong></div>
|
||||
<div class="doc-card-body">
|
||||
<form method="POST" action="{{ route('documentation.documents.store') }}" enctype="multipart/form-data">
|
||||
@include('documentation::partials.document-form')
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -4,24 +4,27 @@
|
||||
|
||||
@section('content')
|
||||
<div class="doc-page">
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => 'Edit document',
|
||||
'subtitle' => 'Maintain document metadata, workflow status, version and attachment details.',
|
||||
])
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header"><strong>{{ $document->title }}</strong></div>
|
||||
<div class="doc-card-body">
|
||||
<form method="POST" action="{{ route('documentation.documents.update', $document) }}" enctype="multipart/form-data">
|
||||
@include('documentation::partials.document-form')
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => 'Edit document',
|
||||
'subtitle' => 'Maintain document metadata, workflow status, version and attachment details.',
|
||||
])
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header"><strong>{{ $document->title }}</strong></div>
|
||||
<div class="doc-card-body">
|
||||
<form method="POST" action="{{ route('documentation.documents.update', $document) }}"
|
||||
enctype="multipart/form-data">
|
||||
@include('documentation::partials.document-form')
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@foreach($document->attachments as $attachment)
|
||||
<form id="delete-attachment-{{ $attachment->id }}" method="POST" action="{{ route('documentation.documents.attachments.destroy', [$document, $attachment]) }}" onsubmit="return confirm('Remove this attachment?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
</form>
|
||||
@endforeach
|
||||
@foreach ($document->attachments as $attachment)
|
||||
<form id="delete-attachment-{{ $attachment->id }}" method="POST"
|
||||
action="{{ route('documentation.documents.attachments.destroy', [$document, $attachment]) }}"
|
||||
onsubmit="return confirm('Remove this attachment?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
</form>
|
||||
@endforeach
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -4,91 +4,102 @@
|
||||
|
||||
@section('content')
|
||||
<div class="doc-page">
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => 'Documentation Centre',
|
||||
'subtitle' => 'Create, control, publish, and archive operational documents from a clean professional register.',
|
||||
])
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => 'Documentation Centre',
|
||||
'subtitle' =>
|
||||
'Create, control, publish, and archive operational documents from a clean professional register.',
|
||||
])
|
||||
|
||||
@if(session('status'))
|
||||
<div class="doc-alert">{{ session('status') }}</div>
|
||||
@endif
|
||||
@if (session('status'))
|
||||
<div class="doc-alert">{{ session('status') }}</div>
|
||||
@endif
|
||||
|
||||
<section class="doc-grid stats">
|
||||
<article class="doc-card doc-stat"><strong>{{ $stats['total'] }}</strong><span>Total documents</span></article>
|
||||
<article class="doc-card doc-stat"><strong>{{ $stats['published'] }}</strong><span>Published</span></article>
|
||||
<article class="doc-card doc-stat"><strong>{{ $stats['drafts'] }}</strong><span>Drafts</span></article>
|
||||
<article class="doc-card doc-stat"><strong>{{ $stats['categories'] }}</strong><span>Categories</span></article>
|
||||
</section>
|
||||
<section class="doc-grid stats">
|
||||
<article class="doc-card doc-stat"><strong>{{ $stats['total'] }}</strong><span>Total documents</span></article>
|
||||
<article class="doc-card doc-stat"><strong>{{ $stats['published'] }}</strong><span>Published</span></article>
|
||||
<article class="doc-card doc-stat"><strong>{{ $stats['drafts'] }}</strong><span>Drafts</span></article>
|
||||
<article class="doc-card doc-stat"><strong>{{ $stats['categories'] }}</strong><span>Categories</span></article>
|
||||
</section>
|
||||
|
||||
@if($featuredDocuments->isNotEmpty())
|
||||
<section class="doc-card" style="margin-bottom:18px;">
|
||||
<div class="doc-card-header"><strong>Featured documents</strong></div>
|
||||
<div class="doc-card-body doc-grid" style="grid-template-columns:repeat(auto-fit,minmax(220px,1fr));">
|
||||
@foreach($featuredDocuments as $featured)
|
||||
<a class="doc-card doc-card-body" style="text-decoration:none;color:inherit;box-shadow:none;" href="{{ route('documentation.documents.show', $featured) }}">
|
||||
<span class="doc-badge {{ $featured->status }}">{{ ucfirst($featured->status) }}</span>
|
||||
<h3 style="margin:12px 0 6px;">{{ $featured->title }}</h3>
|
||||
<p class="doc-muted" style="margin:0;">{{ $featured->summary ?: 'No summary provided.' }}</p>
|
||||
</a>
|
||||
@endforeach
|
||||
@if ($featuredDocuments->isNotEmpty())
|
||||
<section class="doc-card" style="margin-bottom:18px;">
|
||||
<div class="doc-card-header"><strong>Featured documents</strong></div>
|
||||
<div class="doc-card-body doc-grid" style="grid-template-columns:repeat(auto-fit,minmax(220px,1fr));">
|
||||
@foreach ($featuredDocuments as $featured)
|
||||
<a class="doc-card doc-card-body" style="text-decoration:none;color:inherit;box-shadow:none;"
|
||||
href="{{ route('documentation.documents.show', $featured) }}">
|
||||
<span class="doc-badge {{ $featured->status }}">{{ ucfirst($featured->status) }}</span>
|
||||
<h3 style="margin:12px 0 6px;">{{ $featured->title }}</h3>
|
||||
<p class="doc-muted" style="margin:0;">{{ $featured->summary ?: 'No summary provided.' }}</p>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header">
|
||||
<strong>Document register</strong>
|
||||
<a class="doc-button success" href="{{ route('documentation.documents.create') }}">New document</a>
|
||||
</div>
|
||||
<div class="doc-card-body">
|
||||
<form class="doc-filter" method="GET" action="{{ route('documentation.documents.index') }}">
|
||||
<input class="doc-input" name="search" value="{{ request('search') }}"
|
||||
placeholder="Search title, reference, summary or body">
|
||||
<select class="doc-select" name="status">
|
||||
<option value="">All statuses</option>
|
||||
@foreach ($statuses as $value => $label)
|
||||
<option value="{{ $value }}" @selected(request('status') === $value)>{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<select class="doc-select" name="category">
|
||||
<option value="">All categories</option>
|
||||
@foreach ($categories as $category)
|
||||
<option value="{{ $category->id }}" @selected((string) request('category') === (string) $category->id)>{{ $category->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<button class="doc-button primary" type="submit">Filter</button>
|
||||
</form>
|
||||
|
||||
<table class="doc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Document</th>
|
||||
<th>Category</th>
|
||||
<th>Status</th>
|
||||
<th>Version</th>
|
||||
<th>Updated</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($documents as $document)
|
||||
<tr>
|
||||
<td>
|
||||
<a
|
||||
href="{{ route('documentation.documents.show', $document) }}">{{ $document->title }}</a>
|
||||
<div class="doc-muted">{{ $document->reference ?: 'No reference' }} ·
|
||||
{{ $document->summary ?: 'No summary' }}</div>
|
||||
</td>
|
||||
<td>{{ $document->category?->name ?: 'Uncategorised' }}</td>
|
||||
<td><span
|
||||
class="doc-badge {{ $document->status }}">{{ ucfirst($document->status) }}</span>
|
||||
</td>
|
||||
<td>{{ $document->version }}</td>
|
||||
<td>{{ $document->updated_at?->format('d M Y H:i') }}</td>
|
||||
<td><a class="doc-link"
|
||||
href="{{ route('documentation.documents.edit', $document) }}">Edit</a></td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="doc-muted">No documents found.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="margin-top:18px;">{{ $documents->links() }}</div>
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header">
|
||||
<strong>Document register</strong>
|
||||
<a class="doc-button success" href="{{ route('documentation.documents.create') }}">New document</a>
|
||||
</div>
|
||||
<div class="doc-card-body">
|
||||
<form class="doc-filter" method="GET" action="{{ route('documentation.documents.index') }}">
|
||||
<input class="doc-input" name="search" value="{{ request('search') }}" placeholder="Search title, reference, summary or body">
|
||||
<select class="doc-select" name="status">
|
||||
<option value="">All statuses</option>
|
||||
@foreach($statuses as $value => $label)
|
||||
<option value="{{ $value }}" @selected(request('status') === $value)>{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<select class="doc-select" name="category">
|
||||
<option value="">All categories</option>
|
||||
@foreach($categories as $category)
|
||||
<option value="{{ $category->id }}" @selected((string) request('category') === (string) $category->id)>{{ $category->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<button class="doc-button primary" type="submit">Filter</button>
|
||||
</form>
|
||||
|
||||
<table class="doc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Document</th>
|
||||
<th>Category</th>
|
||||
<th>Status</th>
|
||||
<th>Version</th>
|
||||
<th>Updated</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($documents as $document)
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ route('documentation.documents.show', $document) }}">{{ $document->title }}</a>
|
||||
<div class="doc-muted">{{ $document->reference ?: 'No reference' }} · {{ $document->summary ?: 'No summary' }}</div>
|
||||
</td>
|
||||
<td>{{ $document->category?->name ?: 'Uncategorised' }}</td>
|
||||
<td><span class="doc-badge {{ $document->status }}">{{ ucfirst($document->status) }}</span></td>
|
||||
<td>{{ $document->version }}</td>
|
||||
<td>{{ $document->updated_at?->format('d M Y H:i') }}</td>
|
||||
<td><a class="doc-link" href="{{ route('documentation.documents.edit', $document) }}">Edit</a></td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="doc-muted">No documents found.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="margin-top:18px;">{{ $documents->links() }}</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -4,52 +4,67 @@
|
||||
|
||||
@section('content')
|
||||
<div class="doc-page">
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => $document->title,
|
||||
'subtitle' => $document->summary ?: 'Controlled documentation record with versioning, attachments and Markdown rendering.',
|
||||
'actions' => [
|
||||
['label' => 'Edit document', 'url' => route('documentation.documents.edit', $document), 'class' => 'primary'],
|
||||
['label' => 'Back to documents', 'url' => route('documentation.documents.index'), 'class' => 'secondary'],
|
||||
],
|
||||
])
|
||||
@include('documentation::partials.hero', [
|
||||
'title' => $document->title,
|
||||
'subtitle' =>
|
||||
$document->summary ?:
|
||||
'Controlled documentation record with versioning, attachments and Markdown rendering.',
|
||||
'actions' => [
|
||||
[
|
||||
'label' => 'Edit document',
|
||||
'url' => route('documentation.documents.edit', $document),
|
||||
'class' => 'primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Back to documents',
|
||||
'url' => route('documentation.documents.index'),
|
||||
'class' => 'secondary',
|
||||
],
|
||||
],
|
||||
])
|
||||
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header">
|
||||
<div>
|
||||
<strong>{{ $document->reference ?: 'Document' }}</strong>
|
||||
<div class="doc-muted">Version {{ $document->version }} @if($document->category) · {{ $document->category->name }} @endif</div>
|
||||
</div>
|
||||
<span class="doc-badge {{ $document->status }}">{{ ucfirst($document->status) }}</span>
|
||||
</div>
|
||||
<div class="doc-card-body">
|
||||
<article class="doc-content">{!! $document->rendered_body !!}</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@if($document->attachment_name || $document->attachments->isNotEmpty())
|
||||
<section class="doc-card" style="margin-top: 18px;">
|
||||
<div class="doc-card-header"><strong>Attachments</strong></div>
|
||||
<div class="doc-card-body">
|
||||
<div class="doc-attachment-list">
|
||||
@if($document->attachment_name)
|
||||
<div class="doc-attachment-item">
|
||||
<div>
|
||||
<a class="doc-link" href="{{ route('documentation.documents.download', $document) }}">{{ $document->attachment_name }}</a>
|
||||
<span class="doc-muted">Primary file</span>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@foreach($document->attachments as $attachment)
|
||||
<div class="doc-attachment-item">
|
||||
<div>
|
||||
<a class="doc-link" href="{{ route('documentation.documents.attachments.download', [$document, $attachment]) }}">{{ $attachment->original_name }}</a>
|
||||
<span class="doc-muted">{{ $attachment->size_label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
<section class="doc-card">
|
||||
<div class="doc-card-header">
|
||||
<div>
|
||||
<strong>{{ $document->reference ?: 'Document' }}</strong>
|
||||
<div class="doc-muted">Version {{ $document->version }} @if ($document->category)
|
||||
· {{ $document->category->name }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<span class="doc-badge {{ $document->status }}">{{ ucfirst($document->status) }}</span>
|
||||
</div>
|
||||
<div class="doc-card-body">
|
||||
<article class="doc-content">{!! $document->rendered_body !!}</article>
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
||||
@if ($document->attachment_name || $document->attachments->isNotEmpty())
|
||||
<section class="doc-card" style="margin-top: 18px;">
|
||||
<div class="doc-card-header"><strong>Attachments</strong></div>
|
||||
<div class="doc-card-body">
|
||||
<div class="doc-attachment-list">
|
||||
@if ($document->attachment_name)
|
||||
<div class="doc-attachment-item">
|
||||
<div>
|
||||
<a class="doc-link"
|
||||
href="{{ route('documentation.documents.download', $document) }}">{{ $document->attachment_name }}</a>
|
||||
<span class="doc-muted">Primary file</span>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@foreach ($document->attachments as $attachment)
|
||||
<div class="doc-attachment-item">
|
||||
<div>
|
||||
<a class="doc-link"
|
||||
href="{{ route('documentation.documents.attachments.download', [$document, $attachment]) }}">{{ $attachment->original_name }}</a>
|
||||
<span class="doc-muted">{{ $attachment->size_label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,40 +1,58 @@
|
||||
@csrf
|
||||
@if($category->exists)
|
||||
@if ($category->exists)
|
||||
@method('PUT')
|
||||
@endif
|
||||
<div class="doc-form-grid">
|
||||
<div class="doc-field">
|
||||
<label class="doc-label" for="name">Name</label>
|
||||
<input class="doc-input" id="name" name="name" value="{{ old('name', $category->name) }}" required maxlength="150">
|
||||
@error('name') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
<input class="doc-input" id="name" name="name" value="{{ old('name', $category->name) }}" required
|
||||
maxlength="150">
|
||||
@error('name')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field">
|
||||
<label class="doc-label" for="slug">Slug</label>
|
||||
<input class="doc-input" id="slug" name="slug" value="{{ old('slug', $category->slug) }}" maxlength="180" placeholder="Auto-generated when blank">
|
||||
@error('slug') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
<input class="doc-input" id="slug" name="slug" value="{{ old('slug', $category->slug) }}"
|
||||
maxlength="180" placeholder="Auto-generated when blank">
|
||||
@error('slug')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field">
|
||||
<label class="doc-label" for="icon">Icon</label>
|
||||
<input class="doc-input" id="icon" name="icon" value="{{ old('icon', $category->icon) }}" maxlength="80" placeholder="book-open, file-check, shield">
|
||||
@error('icon') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
<input class="doc-input" id="icon" name="icon" value="{{ old('icon', $category->icon) }}" maxlength="80"
|
||||
placeholder="book-open, file-check, shield">
|
||||
@error('icon')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field">
|
||||
<label class="doc-label" for="colour">Colour</label>
|
||||
<input class="doc-input" id="colour" name="colour" value="{{ old('colour', $category->colour ?: 'emerald') }}" required maxlength="32">
|
||||
@error('colour') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
<input class="doc-input" id="colour" name="colour"
|
||||
value="{{ old('colour', $category->colour ?: 'emerald') }}" required maxlength="32">
|
||||
@error('colour')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field">
|
||||
<label class="doc-label" for="sort_order">Sort order</label>
|
||||
<input class="doc-input" id="sort_order" name="sort_order" type="number" min="0" value="{{ old('sort_order', $category->sort_order ?: 0) }}">
|
||||
@error('sort_order') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
<input class="doc-input" id="sort_order" name="sort_order" type="number" min="0"
|
||||
value="{{ old('sort_order', $category->sort_order ?: 0) }}">
|
||||
@error('sort_order')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field" style="display:flex;align-items:end;">
|
||||
<label><input type="checkbox" name="is_active" value="1" @checked(old('is_active', $category->is_active ?? true))> Active category</label>
|
||||
<label><input type="checkbox" name="is_active" value="1" @checked(old('is_active', $category->is_active ?? true))> Active
|
||||
category</label>
|
||||
</div>
|
||||
<div class="doc-field full">
|
||||
<label class="doc-label" for="description">Description</label>
|
||||
<textarea class="doc-textarea" id="description" name="description" style="min-height:140px;">{{ old('description', $category->description) }}</textarea>
|
||||
@error('description') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
@error('description')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
<div class="doc-actions" style="margin-top:22px;">
|
||||
|
||||
@@ -1,52 +1,71 @@
|
||||
@csrf
|
||||
@if($document->exists)
|
||||
@if ($document->exists)
|
||||
@method('PUT')
|
||||
@endif
|
||||
|
||||
<div class="doc-form-grid">
|
||||
<div class="doc-field">
|
||||
<label class="doc-label" for="title">Title</label>
|
||||
<input class="doc-input" id="title" name="title" value="{{ old('title', $document->title) }}" required maxlength="220">
|
||||
@error('title') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
<input class="doc-input" id="title" name="title" value="{{ old('title', $document->title) }}" required
|
||||
maxlength="220">
|
||||
@error('title')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field">
|
||||
<label class="doc-label" for="slug">Slug</label>
|
||||
<input class="doc-input" id="slug" name="slug" value="{{ old('slug', $document->slug) }}" maxlength="240" placeholder="Auto-generated when blank">
|
||||
@error('slug') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
<input class="doc-input" id="slug" name="slug" value="{{ old('slug', $document->slug) }}"
|
||||
maxlength="240" placeholder="Auto-generated when blank">
|
||||
@error('slug')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field">
|
||||
<label class="doc-label" for="documentation_category_id">Category</label>
|
||||
<select class="doc-select" id="documentation_category_id" name="documentation_category_id">
|
||||
<option value="">No category</option>
|
||||
@foreach($categories as $category)
|
||||
@foreach ($categories as $category)
|
||||
<option value="{{ $category->id }}" @selected((string) old('documentation_category_id', $document->documentation_category_id) === (string) $category->id)>{{ $category->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('documentation_category_id') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
@error('documentation_category_id')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field">
|
||||
<label class="doc-label" for="status">Status</label>
|
||||
<select class="doc-select" id="status" name="status" required>
|
||||
@foreach($statuses as $value => $label)
|
||||
@foreach ($statuses as $value => $label)
|
||||
<option value="{{ $value }}" @selected(old('status', $document->status) === $value)>{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('status') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
@error('status')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field">
|
||||
<label class="doc-label" for="version">Version</label>
|
||||
<input class="doc-input" id="version" name="version" value="{{ old('version', $document->version ?: '1.0.0') }}" required maxlength="50">
|
||||
@error('version') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
<input class="doc-input" id="version" name="version"
|
||||
value="{{ old('version', $document->version ?: '1.0.0') }}" required maxlength="50">
|
||||
@error('version')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field">
|
||||
<label class="doc-label" for="reference">Reference</label>
|
||||
<input class="doc-input" id="reference" name="reference" value="{{ old('reference', $document->reference) }}" maxlength="100" placeholder="SOP-001, POL-002, etc.">
|
||||
@error('reference') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
<input class="doc-input" id="reference" name="reference" value="{{ old('reference', $document->reference) }}"
|
||||
maxlength="100" placeholder="SOP-001, POL-002, etc.">
|
||||
@error('reference')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field full">
|
||||
<label class="doc-label" for="summary">Summary</label>
|
||||
<input class="doc-input" id="summary" name="summary" value="{{ old('summary', $document->summary) }}" maxlength="500">
|
||||
@error('summary') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
<input class="doc-input" id="summary" name="summary" value="{{ old('summary', $document->summary) }}"
|
||||
maxlength="500">
|
||||
@error('summary')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field full">
|
||||
<label class="doc-label" for="body">Document body</label>
|
||||
@@ -60,47 +79,66 @@
|
||||
<button type="button" data-md-before="1. " data-md-after="" title="Numbered list">1.</button>
|
||||
<button type="button" data-md-before="[" data-md-after="](https://)" title="Link">Link</button>
|
||||
<button type="button" data-md-before="`" data-md-after="`" title="Inline code">Code</button>
|
||||
<button type="button" data-md-before="```php " data-md-after=" ```" title="Code block">Code block</button>
|
||||
<button type="button" data-md-before="| Column | Column | | --- | --- | | Value | Value |" data-md-after="" title="Table">Table</button>
|
||||
<button type="button" data-md-before="```php " data-md-after=" ```" title="Code block">Code
|
||||
block</button>
|
||||
<button type="button" data-md-before="| Column | Column | | --- | --- | | Value | Value |"
|
||||
data-md-after="" title="Table">Table</button>
|
||||
</div>
|
||||
<textarea class="doc-textarea" id="body" name="body" data-markdown-textarea>{{ old('body', $document->body) }}</textarea>
|
||||
<p class="doc-muted editor-help">Markdown is supported, including fenced code blocks such as <code>```php</code>, tables, lists, links, and headings.</p>
|
||||
<p class="doc-muted editor-help">Markdown is supported, including fenced code blocks such as
|
||||
<code>```php</code>, tables, lists, links, and headings.
|
||||
</p>
|
||||
</div>
|
||||
@error('body') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
@error('body')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field full">
|
||||
<label class="doc-label" for="attachment">Primary attachment</label>
|
||||
<input class="doc-input" id="attachment" name="attachment" type="file">
|
||||
<p class="doc-muted">Primary Markdown files can be imported into the document body when the body is blank.</p>
|
||||
@if($document->attachment_name)
|
||||
<p class="doc-muted">Current primary file: <a class="doc-link" href="{{ route('documentation.documents.download', $document) }}">{{ $document->attachment_name }}</a></p>
|
||||
<label><input type="checkbox" name="remove_attachment" value="1"> Remove current primary attachment</label>
|
||||
@if ($document->attachment_name)
|
||||
<p class="doc-muted">Current primary file: <a class="doc-link"
|
||||
href="{{ route('documentation.documents.download', $document) }}">{{ $document->attachment_name }}</a>
|
||||
</p>
|
||||
<label><input type="checkbox" name="remove_attachment" value="1"> Remove current primary
|
||||
attachment</label>
|
||||
@endif
|
||||
@error('attachment') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
@error('attachment')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="doc-field full">
|
||||
<label class="doc-label" for="attachments">Additional attachments</label>
|
||||
<input class="doc-input" id="attachments" name="attachments[]" type="file" multiple>
|
||||
<p class="doc-muted">Attach supporting files such as PDFs, screenshots, spreadsheets, Word documents, Markdown, CSV, and images.</p>
|
||||
@error('attachments') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
@error('attachments.*') <div class="doc-error">{{ $message }}</div> @enderror
|
||||
<p class="doc-muted">Attach supporting files such as PDFs, screenshots, spreadsheets, Word documents, Markdown,
|
||||
CSV, and images.</p>
|
||||
@error('attachments')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
@error('attachments.*')
|
||||
<div class="doc-error">{{ $message }}</div>
|
||||
@enderror
|
||||
|
||||
@if($document->exists && $document->attachments->isNotEmpty())
|
||||
@if ($document->exists && $document->attachments->isNotEmpty())
|
||||
<div class="doc-attachment-list" style="margin-top: 14px;">
|
||||
@foreach($document->attachments as $attachment)
|
||||
@foreach ($document->attachments as $attachment)
|
||||
<div class="doc-attachment-item">
|
||||
<div>
|
||||
<a class="doc-link" href="{{ route('documentation.documents.attachments.download', [$document, $attachment]) }}">{{ $attachment->original_name }}</a>
|
||||
<a class="doc-link"
|
||||
href="{{ route('documentation.documents.attachments.download', [$document, $attachment]) }}">{{ $attachment->original_name }}</a>
|
||||
<span class="doc-muted">{{ $attachment->size_label }}</span>
|
||||
</div>
|
||||
<button class="doc-button danger" type="submit" form="delete-attachment-{{ $attachment->id }}">Remove</button>
|
||||
<button class="doc-button danger" type="submit"
|
||||
form="delete-attachment-{{ $attachment->id }}">Remove</button>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="doc-field full">
|
||||
<label><input type="checkbox" name="is_featured" value="1" @checked(old('is_featured', $document->is_featured))> Feature this document on the dashboard</label>
|
||||
<label><input type="checkbox" name="is_featured" value="1" @checked(old('is_featured', $document->is_featured))> Feature this
|
||||
document on the dashboard</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="doc-actions" style="margin-top:22px;">
|
||||
|
||||
@@ -4,13 +4,17 @@
|
||||
<div>
|
||||
<p class="doc-eyebrow">{{ $eyebrow ?? 'Documentation workflow' }}</p>
|
||||
<h1>{{ $title ?? 'Documentation' }}</h1>
|
||||
<p>{{ $subtitle ?? 'Manage controlled documents, policies, guides, files, and operational knowledge in one professional workspace.' }}</p>
|
||||
<p>{{ $subtitle ?? 'Manage controlled documents, policies, guides, files, and operational knowledge in one professional workspace.' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="doc-actions">
|
||||
<a class="doc-button success" href="{{ route('documentation.documents.create') }}" wire:navigate>Create document</a>
|
||||
<a class="doc-button category" href="{{ route('documentation.categories.index') }}" wire:navigate>Manage categories</a>
|
||||
<a class="doc-button register" href="{{ route('documentation.documents.index') }}" wire:navigate>Document register</a>
|
||||
<a class="doc-button success" href="{{ route('documentation.documents.create') }}" wire:navigate>Create
|
||||
document</a>
|
||||
<a class="doc-button category" href="{{ route('documentation.categories.index') }}" wire:navigate>Manage
|
||||
categories</a>
|
||||
<a class="doc-button register" href="{{ route('documentation.documents.index') }}" wire:navigate>Document
|
||||
register</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
@props([
|
||||
'id',
|
||||
'model',
|
||||
'label' => null,
|
||||
'required' => false,
|
||||
'placeholder' => 'Select date',
|
||||
])
|
||||
@props(['id', 'model', 'label' => null, 'required' => false, 'placeholder' => 'Select date'])
|
||||
|
||||
<div>
|
||||
@if ($label)
|
||||
@@ -17,17 +11,11 @@
|
||||
@endif
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<input
|
||||
id="{{ $id }}"
|
||||
type="text"
|
||||
value="{{ $attributes->get('value') }}"
|
||||
data-helpdesk-flatpickr
|
||||
data-wire-model="{{ $model }}"
|
||||
placeholder="{{ $placeholder }}"
|
||||
autocomplete="off"
|
||||
<input id="{{ $id }}" type="text" value="{{ $attributes->get('value') }}" data-helpdesk-flatpickr
|
||||
data-wire-model="{{ $model }}" placeholder="{{ $placeholder }}" autocomplete="off"
|
||||
{{ $attributes->except('value')->merge([
|
||||
'class' => 'block w-full rounded-xl border border-zinc-700 bg-zinc-950 px-4 py-3 text-sm text-zinc-100 shadow-sm outline-none transition-colors duration-150 placeholder:text-zinc-500 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/40',
|
||||
]) }}
|
||||
/>
|
||||
'class' =>
|
||||
'block w-full rounded-xl border border-zinc-700 bg-zinc-950 px-4 py-3 text-sm text-zinc-100 shadow-sm outline-none transition-colors duration-150 placeholder:text-zinc-500 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/40',
|
||||
]) }} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,12 +10,10 @@
|
||||
$initialValue = $model ? data_get($this, $model, '') : '';
|
||||
@endphp
|
||||
|
||||
<div
|
||||
{{ $attributes->whereDoesntStartWith('wire:model')->merge(['class' => 'space-y-2']) }}
|
||||
@if($model) data-helpdesk-editor-model="{{ $model }}" @endif
|
||||
<div {{ $attributes->whereDoesntStartWith('wire:model')->merge(['class' => 'space-y-2']) }}
|
||||
@if ($model) data-helpdesk-editor-model="{{ $model }}" @endif
|
||||
x-on:helpdesk-editor-reset.window="if ($event.detail.model === model) clear()"
|
||||
x-on:helpdesk-editor-sync.window="if ($event.detail.model === model) sync()"
|
||||
x-data="{
|
||||
x-on:helpdesk-editor-sync.window="if ($event.detail.model === model) sync()" x-data="{
|
||||
model: @js($model),
|
||||
value: @js($initialValue),
|
||||
sync() {
|
||||
@@ -39,39 +37,41 @@
|
||||
init() {
|
||||
this.$refs.editor.innerHTML = this.value || '';
|
||||
}
|
||||
}"
|
||||
>
|
||||
}">
|
||||
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-200">{{ $label }}</label>
|
||||
|
||||
@if($model)
|
||||
@if ($model)
|
||||
<input type="hidden" name="{{ $model }}" x-bind:value="value">
|
||||
@endif
|
||||
|
||||
<div class="overflow-hidden rounded-xl border border-zinc-300 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-950" wire:ignore>
|
||||
<div class="overflow-hidden rounded-xl border border-zinc-300 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-950"
|
||||
wire:ignore>
|
||||
<div class="flex flex-wrap gap-1 border-b border-zinc-200 bg-zinc-50 p-2 dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<button type="button" x-on:click="command('bold')" class="rounded-lg px-2 py-1 text-sm font-bold hover:bg-zinc-200 dark:hover:bg-zinc-800">B</button>
|
||||
<button type="button" x-on:click="command('italic')" class="rounded-lg px-2 py-1 text-sm italic hover:bg-zinc-200 dark:hover:bg-zinc-800">I</button>
|
||||
<button type="button" x-on:click="command('underline')" class="rounded-lg px-2 py-1 text-sm underline hover:bg-zinc-200 dark:hover:bg-zinc-800">U</button>
|
||||
<button type="button" x-on:click="command('insertUnorderedList')" class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">• List</button>
|
||||
<button type="button" x-on:click="command('insertOrderedList')" class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">1. List</button>
|
||||
<button type="button" x-on:click="command('formatBlock', 'blockquote')" class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">Quote</button>
|
||||
<button type="button" x-on:click="const url = prompt('URL'); if (url) command('createLink', url)" class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">Link</button>
|
||||
<button type="button" x-on:click="command('removeFormat')" class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">Clear</button>
|
||||
<button type="button" x-on:click="command('bold')"
|
||||
class="rounded-lg px-2 py-1 text-sm font-bold hover:bg-zinc-200 dark:hover:bg-zinc-800">B</button>
|
||||
<button type="button" x-on:click="command('italic')"
|
||||
class="rounded-lg px-2 py-1 text-sm italic hover:bg-zinc-200 dark:hover:bg-zinc-800">I</button>
|
||||
<button type="button" x-on:click="command('underline')"
|
||||
class="rounded-lg px-2 py-1 text-sm underline hover:bg-zinc-200 dark:hover:bg-zinc-800">U</button>
|
||||
<button type="button" x-on:click="command('insertUnorderedList')"
|
||||
class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">• List</button>
|
||||
<button type="button" x-on:click="command('insertOrderedList')"
|
||||
class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">1. List</button>
|
||||
<button type="button" x-on:click="command('formatBlock', 'blockquote')"
|
||||
class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">Quote</button>
|
||||
<button type="button" x-on:click="const url = prompt('URL'); if (url) command('createLink', url)"
|
||||
class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">Link</button>
|
||||
<button type="button" x-on:click="command('removeFormat')"
|
||||
class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">Clear</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
x-ref="editor"
|
||||
contenteditable="true"
|
||||
x-on:input.debounce.150ms="sync"
|
||||
x-on:keyup="sync"
|
||||
x-on:paste="$nextTick(() => sync())"
|
||||
x-on:blur="sync"
|
||||
<div x-ref="editor" contenteditable="true" x-on:input.debounce.150ms="sync" x-on:keyup="sync"
|
||||
x-on:paste="$nextTick(() => sync())" x-on:blur="sync"
|
||||
class="prose prose-sm min-h-[{{ (int) $rows * 28 }}px] max-w-none bg-white p-4 text-sm text-zinc-900 outline-none dark:prose-invert dark:bg-zinc-950 dark:text-zinc-100"
|
||||
data-placeholder="{{ $placeholder }}"
|
||||
></div>
|
||||
data-placeholder="{{ $placeholder }}"></div>
|
||||
</div>
|
||||
|
||||
@if($model)
|
||||
@if ($model)
|
||||
@error($model)
|
||||
<div class="text-sm text-red-600">{{ $message }}</div>
|
||||
@enderror
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
|
||||
<thead class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<thead
|
||||
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<tr>
|
||||
<th class="px-4 py-3">Name</th>
|
||||
<th class="px-4 py-3">Slug</th>
|
||||
@@ -15,22 +16,28 @@
|
||||
<tr>
|
||||
<td class="px-4 py-3 font-medium text-zinc-900 dark:text-zinc-100">{{ $item->name }}</td>
|
||||
<td class="px-4 py-3 font-mono text-xs text-zinc-500">{{ $item->slug }}</td>
|
||||
<td class="px-4 py-3"><flux:badge color="{{ $item->color }}">{{ $item->color }}</flux:badge></td>
|
||||
<td class="px-4 py-3">
|
||||
<flux:badge color="{{ ($item->is_default ?? false) ? 'emerald' : 'zinc' }}">
|
||||
{{ ($item->is_default ?? false) ? 'Default' : 'No' }}
|
||||
<flux:badge color="{{ $item->color }}">{{ $item->color }}</flux:badge>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<flux:badge color="{{ $item->is_default ?? false ? 'emerald' : 'zinc' }}">
|
||||
{{ $item->is_default ?? false ? 'Default' : 'No' }}
|
||||
</flux:badge>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-zinc-500">{{ $item->tickets_count ?? 0 }}</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<div class="flex justify-end gap-2">
|
||||
<button type="button" wire:click="{{ $edit }}({{ $item->id }})" class="hd-btn hd-btn-sm hd-btn-blue">Edit</button>
|
||||
<button type="button" wire:click="{{ $delete }}({{ $item->id }})" class="hd-btn hd-btn-sm hd-btn-red">Delete</button>
|
||||
<button type="button" wire:click="{{ $edit }}({{ $item->id }})"
|
||||
class="hd-btn hd-btn-sm hd-btn-blue">Edit</button>
|
||||
<button type="button" wire:click="{{ $delete }}({{ $item->id }})"
|
||||
class="hd-btn hd-btn-sm hd-btn-red">Delete</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="px-4 py-10 text-center text-zinc-500">No records yet.</td></tr>
|
||||
<tr>
|
||||
<td colspan="6" class="px-4 py-10 text-center text-zinc-500">No records yet.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
|
||||
<thead class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<tr><th class="px-4 py-3">Project</th><th class="px-4 py-3">Company</th><th class="px-4 py-3">Team</th><th class="px-4 py-3">Default</th><th class="px-4 py-3">Tickets</th><th class="px-4 py-3 text-right">Actions</th></tr>
|
||||
<thead
|
||||
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<tr>
|
||||
<th class="px-4 py-3">Project</th>
|
||||
<th class="px-4 py-3">Company</th>
|
||||
<th class="px-4 py-3">Team</th>
|
||||
<th class="px-4 py-3">Default</th>
|
||||
<th class="px-4 py-3">Tickets</th>
|
||||
<th class="px-4 py-3 text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-zinc-100 dark:divide-zinc-800">
|
||||
@forelse ($items as $item)
|
||||
@@ -9,12 +17,23 @@
|
||||
<td class="px-4 py-3 font-medium text-zinc-900 dark:text-zinc-100">{{ $item->title }}</td>
|
||||
<td class="px-4 py-3 text-zinc-500">{{ $item->company?->name ?? 'Global' }}</td>
|
||||
<td class="px-4 py-3 text-zinc-500">{{ $item->team?->name ?? 'No team' }}</td>
|
||||
<td class="px-4 py-3"><flux:badge color="{{ ($item->is_default ?? false) ? 'emerald' : 'zinc' }}">{{ ($item->is_default ?? false) ? 'Default' : 'No' }}</flux:badge></td>
|
||||
<td class="px-4 py-3">
|
||||
<flux:badge color="{{ $item->is_default ?? false ? 'emerald' : 'zinc' }}">
|
||||
{{ $item->is_default ?? false ? 'Default' : 'No' }}</flux:badge>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-zinc-500">{{ $item->tickets_count ?? 0 }}</td>
|
||||
<td class="px-4 py-3 text-right"><div class="flex justify-end gap-2"><button type="button" wire:click="editProject({{ $item->id }})" class="hd-btn hd-btn-sm hd-btn-blue">Edit</button><button type="button" wire:click="deleteProject({{ $item->id }})" class="hd-btn hd-btn-sm hd-btn-red">Delete</button></div></td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<div class="flex justify-end gap-2"><button type="button"
|
||||
wire:click="editProject({{ $item->id }})"
|
||||
class="hd-btn hd-btn-sm hd-btn-blue">Edit</button><button type="button"
|
||||
wire:click="deleteProject({{ $item->id }})"
|
||||
class="hd-btn hd-btn-sm hd-btn-red">Delete</button></div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="px-4 py-10 text-center text-zinc-500">No projects yet.</td></tr>
|
||||
<tr>
|
||||
<td colspan="6" class="px-4 py-10 text-center text-zinc-500">No projects yet.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
|
||||
<thead class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<tr><th class="px-4 py-3">Name</th><th class="px-4 py-3">Provider</th><th class="px-4 py-3">Project</th><th class="px-4 py-3">Commits</th><th class="px-4 py-3 text-right">Actions</th></tr>
|
||||
<thead
|
||||
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<tr>
|
||||
<th class="px-4 py-3">Name</th>
|
||||
<th class="px-4 py-3">Provider</th>
|
||||
<th class="px-4 py-3">Project</th>
|
||||
<th class="px-4 py-3">Commits</th>
|
||||
<th class="px-4 py-3 text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-zinc-100 dark:divide-zinc-800">
|
||||
@forelse ($items as $item)
|
||||
@@ -10,10 +17,19 @@
|
||||
<td class="px-4 py-3 text-zinc-500">{{ strtoupper($item->provider) }}</td>
|
||||
<td class="px-4 py-3 text-zinc-500">{{ $item->project?->title ?? 'No project' }}</td>
|
||||
<td class="px-4 py-3 text-zinc-500">{{ $item->commits_count ?? 0 }}</td>
|
||||
<td class="px-4 py-3 text-right"><div class="flex justify-end gap-2"><button type="button" wire:click="editRepository({{ $item->id }})" class="hd-btn hd-btn-sm hd-btn-blue">Edit</button><button type="button" wire:click="deleteRepository({{ $item->id }})" wire:confirm="Delete this repository?" class="hd-btn hd-btn-sm hd-btn-red">Delete</button></div></td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<div class="flex justify-end gap-2"><button type="button"
|
||||
wire:click="editRepository({{ $item->id }})"
|
||||
class="hd-btn hd-btn-sm hd-btn-blue">Edit</button><button type="button"
|
||||
wire:click="deleteRepository({{ $item->id }})"
|
||||
wire:confirm="Delete this repository?"
|
||||
class="hd-btn hd-btn-sm hd-btn-red">Delete</button></div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="5" class="px-4 py-10 text-center text-zinc-500">No repositories yet.</td></tr>
|
||||
<tr>
|
||||
<td colspan="5" class="px-4 py-10 text-center text-zinc-500">No repositories yet.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<section class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<section
|
||||
class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="border-b border-zinc-200 bg-zinc-50 px-5 py-4 dark:border-zinc-700 dark:bg-zinc-800/70">
|
||||
<h2 class="text-lg font-semibold text-zinc-900 dark:text-white">{{ $title }}</h2>
|
||||
<p class="mt-1 text-sm text-zinc-500 dark:text-zinc-400">{{ $description }}</p>
|
||||
|
||||
@@ -1,19 +1,37 @@
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
|
||||
<thead class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<tr><th class="px-4 py-3">Team</th><th class="px-4 py-3">Company</th><th class="px-4 py-3">Default</th><th class="px-4 py-3">Tickets</th><th class="px-4 py-3 text-right">Actions</th></tr>
|
||||
<thead
|
||||
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<tr>
|
||||
<th class="px-4 py-3">Team</th>
|
||||
<th class="px-4 py-3">Company</th>
|
||||
<th class="px-4 py-3">Default</th>
|
||||
<th class="px-4 py-3">Tickets</th>
|
||||
<th class="px-4 py-3 text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-zinc-100 dark:divide-zinc-800">
|
||||
@forelse ($items as $item)
|
||||
<tr>
|
||||
<td class="px-4 py-3 font-medium text-zinc-900 dark:text-zinc-100">{{ $item->name }}</td>
|
||||
<td class="px-4 py-3 text-zinc-500">{{ $item->company?->name ?? 'Global' }}</td>
|
||||
<td class="px-4 py-3"><flux:badge color="{{ ($item->is_default ?? false) ? 'emerald' : 'zinc' }}">{{ ($item->is_default ?? false) ? 'Default' : 'No' }}</flux:badge></td>
|
||||
<td class="px-4 py-3">
|
||||
<flux:badge color="{{ $item->is_default ?? false ? 'emerald' : 'zinc' }}">
|
||||
{{ $item->is_default ?? false ? 'Default' : 'No' }}</flux:badge>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-zinc-500">{{ $item->tickets_count ?? 0 }}</td>
|
||||
<td class="px-4 py-3 text-right"><div class="flex justify-end gap-2"><button type="button" wire:click="editTeam({{ $item->id }})" class="hd-btn hd-btn-sm hd-btn-blue">Edit</button><button type="button" wire:click="deleteTeam({{ $item->id }})" class="hd-btn hd-btn-sm hd-btn-red">Delete</button></div></td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<div class="flex justify-end gap-2"><button type="button"
|
||||
wire:click="editTeam({{ $item->id }})"
|
||||
class="hd-btn hd-btn-sm hd-btn-blue">Edit</button><button type="button"
|
||||
wire:click="deleteTeam({{ $item->id }})"
|
||||
class="hd-btn hd-btn-sm hd-btn-red">Delete</button></div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="5" class="px-4 py-10 text-center text-zinc-500">No teams yet.</td></tr>
|
||||
<tr>
|
||||
<td colspan="5" class="px-4 py-10 text-center text-zinc-500">No teams yet.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,184 +1,225 @@
|
||||
<style>
|
||||
.hd-helpdesk-surface .hd-btn,
|
||||
.hd-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: .5rem;
|
||||
border-radius: .85rem;
|
||||
padding: .62rem 1.05rem;
|
||||
font-size: .875rem;
|
||||
line-height: 1.25rem;
|
||||
font-weight: 800;
|
||||
color: #ffffff !important;
|
||||
text-decoration: none !important;
|
||||
border: 1px solid rgba(255, 255, 255, .14);
|
||||
box-shadow: 0 12px 28px rgba(0, 0, 0, .22);
|
||||
transition: background-color .15s ease, transform .15s ease, box-shadow .15s ease;
|
||||
}
|
||||
|
||||
<style>
|
||||
.hd-helpdesk-surface .hd-btn,
|
||||
.hd-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: .5rem;
|
||||
border-radius: .85rem;
|
||||
padding: .62rem 1.05rem;
|
||||
font-size: .875rem;
|
||||
line-height: 1.25rem;
|
||||
font-weight: 800;
|
||||
color: #ffffff !important;
|
||||
text-decoration: none !important;
|
||||
border: 1px solid rgba(255,255,255,.14);
|
||||
box-shadow: 0 12px 28px rgba(0,0,0,.22);
|
||||
transition: background-color .15s ease, transform .15s ease, box-shadow .15s ease;
|
||||
}
|
||||
.hd-helpdesk-surface .hd-btn:hover,
|
||||
.hd-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 16px 34px rgba(0,0,0,.30);
|
||||
}
|
||||
.hd-helpdesk-surface .hd-btn-sm,
|
||||
.hd-btn-sm {
|
||||
border-radius: .65rem;
|
||||
padding: .42rem .78rem;
|
||||
font-size: .8125rem;
|
||||
}
|
||||
.hd-helpdesk-surface .hd-btn-blue,
|
||||
.hd-btn-blue { background: #2563eb !important; }
|
||||
.hd-helpdesk-surface .hd-btn-blue:hover,
|
||||
.hd-btn-blue:hover { background: #1d4ed8 !important; }
|
||||
.hd-helpdesk-surface .hd-btn-green,
|
||||
.hd-btn-green { background: #16a34a !important; }
|
||||
.hd-helpdesk-surface .hd-btn-green:hover,
|
||||
.hd-btn-green:hover { background: #15803d !important; }
|
||||
.hd-helpdesk-surface .hd-btn-amber,
|
||||
.hd-btn-amber { background: #f59e0b !important; }
|
||||
.hd-helpdesk-surface .hd-btn-amber:hover,
|
||||
.hd-btn-amber:hover { background: #d97706 !important; }
|
||||
.hd-helpdesk-surface .hd-btn-red,
|
||||
.hd-btn-red { background: #dc2626 !important; }
|
||||
.hd-helpdesk-surface .hd-btn-red:hover,
|
||||
.hd-btn-red:hover { background: #b91c1c !important; }
|
||||
.hd-helpdesk-surface .hd-btn-violet,
|
||||
.hd-btn-violet { background: #7c3aed !important; }
|
||||
.hd-helpdesk-surface .hd-btn-violet:hover,
|
||||
.hd-btn-violet:hover { background: #6d28d9 !important; }
|
||||
.hd-helpdesk-surface .hd-btn:hover,
|
||||
.hd-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 16px 34px rgba(0, 0, 0, .30);
|
||||
}
|
||||
|
||||
.hd-helpdesk-surface button[type="submit"],
|
||||
.hd-helpdesk-surface button[data-flux-button][type="submit"] {
|
||||
background: #16a34a !important;
|
||||
color: #ffffff !important;
|
||||
border-color: rgba(255,255,255,.14) !important;
|
||||
}
|
||||
.hd-helpdesk-surface button[wire\:click^="reset"],
|
||||
.hd-helpdesk-surface button[wire\:target^="reset"] {
|
||||
background: #f59e0b !important;
|
||||
color: #ffffff !important;
|
||||
border-color: rgba(255,255,255,.14) !important;
|
||||
}
|
||||
.hd-helpdesk-surface button[wire\:click^="edit"],
|
||||
.hd-helpdesk-surface button[wire\:target^="edit"] {
|
||||
background: #2563eb !important;
|
||||
color: #ffffff !important;
|
||||
border-color: rgba(255,255,255,.14) !important;
|
||||
}
|
||||
.hd-helpdesk-surface button[wire\:click^="delete"],
|
||||
.hd-helpdesk-surface button[wire\:target^="delete"] {
|
||||
background: #dc2626 !important;
|
||||
color: #ffffff !important;
|
||||
border-color: rgba(255,255,255,.14) !important;
|
||||
}
|
||||
.hd-helpdesk-surface .hd-btn-sm,
|
||||
.hd-btn-sm {
|
||||
border-radius: .65rem;
|
||||
padding: .42rem .78rem;
|
||||
font-size: .8125rem;
|
||||
}
|
||||
|
||||
.hd-select {
|
||||
width: 100%;
|
||||
min-height: 3.35rem;
|
||||
border-radius: 1rem;
|
||||
border: 1px solid #3f3f46 !important;
|
||||
background: #09090b !important;
|
||||
color: #f4f4f5 !important;
|
||||
padding: .78rem 3rem .78rem 1rem;
|
||||
font-weight: 800;
|
||||
outline: none;
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,.05), 0 12px 26px rgba(0,0,0,.24);
|
||||
}
|
||||
.hd-select:focus {
|
||||
border-color: #60a5fa !important;
|
||||
box-shadow: 0 0 0 3px rgba(96,165,250,.38), inset 0 1px 0 rgba(255,255,255,.05);
|
||||
}
|
||||
.hd-helpdesk-surface .hd-btn-blue,
|
||||
.hd-btn-blue {
|
||||
background: #2563eb !important;
|
||||
}
|
||||
|
||||
.helpdesk-ticket-form,
|
||||
.helpdesk-ticket-form * {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.helpdesk-ticket-form input,
|
||||
.helpdesk-ticket-form textarea,
|
||||
.helpdesk-ticket-form select,
|
||||
.helpdesk-ticket-form .select2-container--default .select2-selection--single,
|
||||
.helpdesk-ticket-form .select2-container--default .select2-selection--multiple,
|
||||
.helpdesk-ticket-form .select2-container--default .select2-selection__rendered,
|
||||
.helpdesk-ticket-form .select2-container--default .select2-selection__choice {
|
||||
background: #09090b !important;
|
||||
background-color: #09090b !important;
|
||||
color: #f4f4f5 !important;
|
||||
border-color: #3f3f46 !important;
|
||||
caret-color: #f4f4f5 !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
.helpdesk-ticket-form input:disabled,
|
||||
.helpdesk-ticket-form textarea:disabled,
|
||||
.helpdesk-ticket-form select:disabled,
|
||||
.helpdesk-ticket-form .select2-container--disabled .select2-selection--single,
|
||||
.helpdesk-ticket-form .select2-container--disabled .select2-selection--multiple,
|
||||
.helpdesk-ticket-form [disabled],
|
||||
.helpdesk-ticket-form [aria-disabled="true"] {
|
||||
background: #09090b !important;
|
||||
background-color: #09090b !important;
|
||||
color: #a1a1aa !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
.helpdesk-ticket-form input:-webkit-autofill,
|
||||
.helpdesk-ticket-form input:-webkit-autofill:hover,
|
||||
.helpdesk-ticket-form input:-webkit-autofill:focus,
|
||||
.helpdesk-ticket-form textarea:-webkit-autofill,
|
||||
.helpdesk-ticket-form textarea:-webkit-autofill:hover,
|
||||
.helpdesk-ticket-form textarea:-webkit-autofill:focus,
|
||||
.helpdesk-ticket-form select:-webkit-autofill,
|
||||
.helpdesk-ticket-form select:-webkit-autofill:hover,
|
||||
.helpdesk-ticket-form select:-webkit-autofill:focus {
|
||||
-webkit-text-fill-color: #f4f4f5 !important;
|
||||
box-shadow: 0 0 0 1000px #09090b inset !important;
|
||||
transition: background-color 9999s ease-in-out 0s;
|
||||
}
|
||||
</style>
|
||||
.hd-helpdesk-surface .hd-btn-blue:hover,
|
||||
.hd-btn-blue:hover {
|
||||
background: #1d4ed8 !important;
|
||||
}
|
||||
|
||||
<div class="hd-helpdesk-surface space-y-6">
|
||||
<div class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="bg-gradient-to-br from-slate-950 via-slate-900 to-blue-950 px-6 py-8 text-white">
|
||||
<div class="grid gap-6 lg:grid-cols-[1fr_320px] lg:items-center">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">Support operations</p>
|
||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight md:text-4xl">Helpdesk dashboard</h1>
|
||||
<p class="mt-3 max-w-3xl text-sm leading-6 text-blue-100/90">Track open workload, closed tickets, response performance, project activity, priorities, Git commits and workflow communication from one professional support workspace.</p>
|
||||
<div class="mt-5 flex flex-wrap gap-2">
|
||||
<a href="{{ route('helpdesk.tickets.create') }}" wire:navigate class="hd-btn hd-btn-green">
|
||||
<i class="fa-solid fa-plus" aria-hidden="true"></i>
|
||||
<span>New Ticket</span>
|
||||
</a>
|
||||
<a href="{{ route('helpdesk.tickets.index') }}" wire:navigate class="hd-btn hd-btn-blue">
|
||||
<i class="fa-solid fa-ticket" aria-hidden="true"></i>
|
||||
<span>Active tickets</span>
|
||||
</a>
|
||||
<a href="{{ route('helpdesk.kanban') }}" wire:navigate class="hd-btn hd-btn-amber">
|
||||
<i class="fa-solid fa-table-columns" aria-hidden="true"></i>
|
||||
<span>Kanban</span>
|
||||
</a>
|
||||
<a href="{{ route('helpdesk.settings') }}" wire:navigate class="hd-btn hd-btn-violet">
|
||||
<i class="fa-solid fa-gear" aria-hidden="true"></i>
|
||||
<span>Settings</span>
|
||||
</a>
|
||||
</div>
|
||||
.hd-helpdesk-surface .hd-btn-green,
|
||||
.hd-btn-green {
|
||||
background: #16a34a !important;
|
||||
}
|
||||
|
||||
.hd-helpdesk-surface .hd-btn-green:hover,
|
||||
.hd-btn-green:hover {
|
||||
background: #15803d !important;
|
||||
}
|
||||
|
||||
.hd-helpdesk-surface .hd-btn-amber,
|
||||
.hd-btn-amber {
|
||||
background: #f59e0b !important;
|
||||
}
|
||||
|
||||
.hd-helpdesk-surface .hd-btn-amber:hover,
|
||||
.hd-btn-amber:hover {
|
||||
background: #d97706 !important;
|
||||
}
|
||||
|
||||
.hd-helpdesk-surface .hd-btn-red,
|
||||
.hd-btn-red {
|
||||
background: #dc2626 !important;
|
||||
}
|
||||
|
||||
.hd-helpdesk-surface .hd-btn-red:hover,
|
||||
.hd-btn-red:hover {
|
||||
background: #b91c1c !important;
|
||||
}
|
||||
|
||||
.hd-helpdesk-surface .hd-btn-violet,
|
||||
.hd-btn-violet {
|
||||
background: #7c3aed !important;
|
||||
}
|
||||
|
||||
.hd-helpdesk-surface .hd-btn-violet:hover,
|
||||
.hd-btn-violet:hover {
|
||||
background: #6d28d9 !important;
|
||||
}
|
||||
|
||||
.hd-helpdesk-surface button[type="submit"],
|
||||
.hd-helpdesk-surface button[data-flux-button][type="submit"] {
|
||||
background: #16a34a !important;
|
||||
color: #ffffff !important;
|
||||
border-color: rgba(255, 255, 255, .14) !important;
|
||||
}
|
||||
|
||||
.hd-helpdesk-surface button[wire\:click^="reset"],
|
||||
.hd-helpdesk-surface button[wire\:target^="reset"] {
|
||||
background: #f59e0b !important;
|
||||
color: #ffffff !important;
|
||||
border-color: rgba(255, 255, 255, .14) !important;
|
||||
}
|
||||
|
||||
.hd-helpdesk-surface button[wire\:click^="edit"],
|
||||
.hd-helpdesk-surface button[wire\:target^="edit"] {
|
||||
background: #2563eb !important;
|
||||
color: #ffffff !important;
|
||||
border-color: rgba(255, 255, 255, .14) !important;
|
||||
}
|
||||
|
||||
.hd-helpdesk-surface button[wire\:click^="delete"],
|
||||
.hd-helpdesk-surface button[wire\:target^="delete"] {
|
||||
background: #dc2626 !important;
|
||||
color: #ffffff !important;
|
||||
border-color: rgba(255, 255, 255, .14) !important;
|
||||
}
|
||||
|
||||
.hd-select {
|
||||
width: 100%;
|
||||
min-height: 3.35rem;
|
||||
border-radius: 1rem;
|
||||
border: 1px solid #3f3f46 !important;
|
||||
background: #09090b !important;
|
||||
color: #f4f4f5 !important;
|
||||
padding: .78rem 3rem .78rem 1rem;
|
||||
font-weight: 800;
|
||||
outline: none;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), 0 12px 26px rgba(0, 0, 0, .24);
|
||||
}
|
||||
|
||||
.hd-select:focus {
|
||||
border-color: #60a5fa !important;
|
||||
box-shadow: 0 0 0 3px rgba(96, 165, 250, .38), inset 0 1px 0 rgba(255, 255, 255, .05);
|
||||
}
|
||||
|
||||
.helpdesk-ticket-form,
|
||||
.helpdesk-ticket-form * {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.helpdesk-ticket-form input,
|
||||
.helpdesk-ticket-form textarea,
|
||||
.helpdesk-ticket-form select,
|
||||
.helpdesk-ticket-form .select2-container--default .select2-selection--single,
|
||||
.helpdesk-ticket-form .select2-container--default .select2-selection--multiple,
|
||||
.helpdesk-ticket-form .select2-container--default .select2-selection__rendered,
|
||||
.helpdesk-ticket-form .select2-container--default .select2-selection__choice {
|
||||
background: #09090b !important;
|
||||
background-color: #09090b !important;
|
||||
color: #f4f4f5 !important;
|
||||
border-color: #3f3f46 !important;
|
||||
caret-color: #f4f4f5 !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.helpdesk-ticket-form input:disabled,
|
||||
.helpdesk-ticket-form textarea:disabled,
|
||||
.helpdesk-ticket-form select:disabled,
|
||||
.helpdesk-ticket-form .select2-container--disabled .select2-selection--single,
|
||||
.helpdesk-ticket-form .select2-container--disabled .select2-selection--multiple,
|
||||
.helpdesk-ticket-form [disabled],
|
||||
.helpdesk-ticket-form [aria-disabled="true"] {
|
||||
background: #09090b !important;
|
||||
background-color: #09090b !important;
|
||||
color: #a1a1aa !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.helpdesk-ticket-form input:-webkit-autofill,
|
||||
.helpdesk-ticket-form input:-webkit-autofill:hover,
|
||||
.helpdesk-ticket-form input:-webkit-autofill:focus,
|
||||
.helpdesk-ticket-form textarea:-webkit-autofill,
|
||||
.helpdesk-ticket-form textarea:-webkit-autofill:hover,
|
||||
.helpdesk-ticket-form textarea:-webkit-autofill:focus,
|
||||
.helpdesk-ticket-form select:-webkit-autofill,
|
||||
.helpdesk-ticket-form select:-webkit-autofill:hover,
|
||||
.helpdesk-ticket-form select:-webkit-autofill:focus {
|
||||
-webkit-text-fill-color: #f4f4f5 !important;
|
||||
box-shadow: 0 0 0 1000px #09090b inset !important;
|
||||
transition: background-color 9999s ease-in-out 0s;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="hd-helpdesk-surface space-y-6">
|
||||
<div
|
||||
class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="bg-gradient-to-br from-slate-950 via-slate-900 to-blue-950 px-6 py-8 text-white">
|
||||
<div class="grid gap-6 lg:grid-cols-[1fr_320px] lg:items-center">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">Support operations</p>
|
||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight md:text-4xl">Helpdesk dashboard</h1>
|
||||
<p class="mt-3 max-w-3xl text-sm leading-6 text-blue-100/90">Track open workload, closed tickets,
|
||||
response performance, project activity, priorities, Git commits and workflow communication from
|
||||
one professional support workspace.</p>
|
||||
<div class="mt-5 flex flex-wrap gap-2">
|
||||
<a href="{{ route('helpdesk.tickets.create') }}" wire:navigate class="hd-btn hd-btn-green">
|
||||
<i class="fa-solid fa-plus" aria-hidden="true"></i>
|
||||
<span>New Ticket</span>
|
||||
</a>
|
||||
<a href="{{ route('helpdesk.tickets.index') }}" wire:navigate class="hd-btn hd-btn-blue">
|
||||
<i class="fa-solid fa-ticket" aria-hidden="true"></i>
|
||||
<span>Active tickets</span>
|
||||
</a>
|
||||
<a href="{{ route('helpdesk.kanban') }}" wire:navigate class="hd-btn hd-btn-amber">
|
||||
<i class="fa-solid fa-table-columns" aria-hidden="true"></i>
|
||||
<span>Kanban</span>
|
||||
</a>
|
||||
<a href="{{ route('helpdesk.settings') }}" wire:navigate class="hd-btn hd-btn-violet">
|
||||
<i class="fa-solid fa-gear" aria-hidden="true"></i>
|
||||
<span>Settings</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-white/10 bg-white/10 p-5 shadow-2xl backdrop-blur">
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
@foreach (range(1, 9) as $cell)
|
||||
<div class="h-14 rounded-xl bg-white/15"></div>
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="mt-4 rounded-xl bg-white/10 p-4">
|
||||
<div class="h-2 rounded-full bg-blue-300/80"></div>
|
||||
<div class="mt-3 h-2 w-3/4 rounded-full bg-emerald-300/80"></div>
|
||||
<div class="mt-3 h-2 w-1/2 rounded-full bg-rose-300/80"></div>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-white/10 bg-white/10 p-5 shadow-2xl backdrop-blur">
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
@foreach (range(1, 9) as $cell)
|
||||
<div class="h-14 rounded-xl bg-white/15"></div>
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="mt-4 rounded-xl bg-white/10 p-4">
|
||||
<div class="h-2 rounded-full bg-blue-300/80"></div>
|
||||
<div class="mt-3 h-2 w-3/4 rounded-full bg-emerald-300/80"></div>
|
||||
<div class="mt-3 h-2 w-1/2 rounded-full bg-rose-300/80"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<livewire:helpdesk.dashboard-widget />
|
||||
</div>
|
||||
|
||||
<livewire:helpdesk.dashboard-widget />
|
||||
</div>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,31 +1,27 @@
|
||||
@php
|
||||
$user = auth()->user();
|
||||
$dashboardRoute = Route::has('platform.dashboard') && method_exists($user, 'isMpbaPlatformAdmin') && $user?->isMpbaPlatformAdmin()
|
||||
? 'platform.dashboard'
|
||||
: (Route::has('dashboard') ? 'dashboard' : null);
|
||||
$dashboardRoute =
|
||||
Route::has('platform.dashboard') && method_exists($user, 'isMpbaPlatformAdmin') && $user?->isMpbaPlatformAdmin()
|
||||
? 'platform.dashboard'
|
||||
: (Route::has('dashboard')
|
||||
? 'dashboard'
|
||||
: null);
|
||||
@endphp
|
||||
|
||||
<header class="sticky top-0 z-40 border-b border-white/10 bg-[#111118]/95 backdrop-blur" role="banner">
|
||||
<div class="flex h-16 items-center justify-between px-4 lg:px-6">
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
<button type="button"
|
||||
class="inline-flex h-10 w-10 items-center justify-center rounded-xl border border-white/10 bg-white/5 text-white lg:hidden"
|
||||
data-wms-mobile-open
|
||||
aria-label="{{ __('Open navigation menu') }}"
|
||||
aria-controls="wms-main-sidebar"
|
||||
aria-expanded="false"
|
||||
>
|
||||
data-wms-mobile-open aria-label="{{ __('Open navigation menu') }}" aria-controls="wms-main-sidebar"
|
||||
aria-expanded="false">
|
||||
<i class="fa-solid fa-bars" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<a
|
||||
href="{{ $dashboardRoute ? route($dashboardRoute) : '#' }}"
|
||||
wire:navigate
|
||||
class="flex min-w-0 items-center gap-3"
|
||||
aria-label="{{ __('Warehouse Management System') }}"
|
||||
>
|
||||
<span class="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-cyan-600 text-sm font-bold text-white shadow-lg shadow-cyan-950/40">
|
||||
<a href="{{ $dashboardRoute ? route($dashboardRoute) : '#' }}" wire:navigate
|
||||
class="flex min-w-0 items-center gap-3" aria-label="{{ __('Warehouse Management System') }}">
|
||||
<span
|
||||
class="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-cyan-600 text-sm font-bold text-white shadow-lg shadow-cyan-950/40">
|
||||
W
|
||||
</span>
|
||||
|
||||
@@ -40,62 +36,63 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="relative flex shrink-0 items-center gap-3" x-data="{ open: false }" x-on:keydown.escape.window="open = false">
|
||||
<button
|
||||
type="button"
|
||||
<div class="relative flex shrink-0 items-center gap-3" x-data="{ open: false }"
|
||||
x-on:keydown.escape.window="open = false">
|
||||
<button type="button"
|
||||
class="flex items-center gap-3 rounded-2xl border border-white/10 bg-white/5 px-3 py-2 text-left transition hover:bg-white/10 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-offset-2 focus:ring-offset-[#111118]"
|
||||
x-on:click="open = ! open"
|
||||
x-bind:aria-expanded="open.toString()"
|
||||
aria-haspopup="menu"
|
||||
data-test="top-user-menu-button"
|
||||
>
|
||||
x-on:click="open = ! open" x-bind:aria-expanded="open.toString()" aria-haspopup="menu"
|
||||
data-test="top-user-menu-button">
|
||||
<span class="hidden text-right sm:block">
|
||||
<span class="block text-sm font-semibold text-white">{{ $user?->name ?? __('User') }}</span>
|
||||
<span class="block text-xs text-white/50">{{ $user?->email ?? '' }}</span>
|
||||
</span>
|
||||
|
||||
<span class="flex h-10 w-10 items-center justify-center rounded-full bg-blue-700 text-xs font-bold text-white shadow-lg shadow-blue-950/40">
|
||||
<span
|
||||
class="flex h-10 w-10 items-center justify-center rounded-full bg-blue-700 text-xs font-bold text-white shadow-lg shadow-blue-950/40">
|
||||
{{ $user?->initials() ?? 'U' }}
|
||||
</span>
|
||||
|
||||
<i class="fa-solid fa-chevron-down hidden text-white/50 sm:block" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<div
|
||||
x-cloak
|
||||
x-show="open"
|
||||
x-transition.origin.top.right
|
||||
x-on:click.outside="open = false"
|
||||
<div x-cloak x-show="open" x-transition.origin.top.right x-on:click.outside="open = false"
|
||||
class="absolute right-0 top-full mt-2 w-72 overflow-hidden rounded-2xl border border-white/10 bg-[#181820] shadow-2xl shadow-black/40"
|
||||
role="menu"
|
||||
>
|
||||
role="menu">
|
||||
<div class="border-b border-white/10 px-4 py-3">
|
||||
<div class="text-sm font-semibold text-white">{{ $user?->name ?? __('User') }}</div>
|
||||
<div class="truncate text-xs text-white/50">{{ $user?->email ?? '' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="p-2">
|
||||
@if($dashboardRoute)
|
||||
<a href="{{ route($dashboardRoute) }}" wire:navigate class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white" role="menuitem">
|
||||
@if ($dashboardRoute)
|
||||
<a href="{{ route($dashboardRoute) }}" wire:navigate
|
||||
class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white"
|
||||
role="menuitem">
|
||||
{{ __('Dashboard') }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if(Route::has('profile.edit'))
|
||||
<a href="{{ route('profile.edit') }}" wire:navigate class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white" role="menuitem">
|
||||
@if (Route::has('profile.edit'))
|
||||
<a href="{{ route('profile.edit') }}" wire:navigate
|
||||
class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white"
|
||||
role="menuitem">
|
||||
{{ __('Profile') }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if(Route::has('settings.index'))
|
||||
<a href="{{ route('settings.index') }}" wire:navigate class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white" role="menuitem">
|
||||
@if (Route::has('settings.index'))
|
||||
<a href="{{ route('settings.index') }}" wire:navigate
|
||||
class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white"
|
||||
role="menuitem">
|
||||
{{ __('Settings') }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('logout') }}" class="mt-2 border-t border-white/10 pt-2">
|
||||
@csrf
|
||||
<button type="submit" class="w-full rounded-xl px-3 py-2 text-left text-sm text-red-300 hover:bg-red-500/10 hover:text-red-200" role="menuitem" data-test="logout-button">
|
||||
<button type="submit"
|
||||
class="w-full rounded-xl px-3 py-2 text-left text-sm text-red-300 hover:bg-red-500/10 hover:text-red-200"
|
||||
role="menuitem" data-test="logout-button">
|
||||
{{ __('Sign out') }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -3,17 +3,23 @@
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="text-sm font-medium text-zinc-500">Total tickets</div>
|
||||
<div class="mt-3 text-4xl font-semibold text-zinc-950 dark:text-white">{{ $totalCount }}</div>
|
||||
<div class="mt-3 h-2 rounded-full bg-zinc-100 dark:bg-zinc-800"><div class="h-2 rounded-full bg-blue-500" style="width: 100%"></div></div>
|
||||
<div class="mt-3 h-2 rounded-full bg-zinc-100 dark:bg-zinc-800">
|
||||
<div class="h-2 rounded-full bg-blue-500" style="width: 100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="text-sm font-medium text-zinc-500">Open tickets</div>
|
||||
<div class="mt-3 text-4xl font-semibold text-blue-700 dark:text-blue-300">{{ $openCount }}</div>
|
||||
<div class="mt-3 h-2 rounded-full bg-zinc-100 dark:bg-zinc-800"><div class="h-2 rounded-full bg-blue-500" style="width: {{ $openPercent }}%"></div></div>
|
||||
<div class="mt-3 h-2 rounded-full bg-zinc-100 dark:bg-zinc-800">
|
||||
<div class="h-2 rounded-full bg-blue-500" style="width: {{ $openPercent }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="text-sm font-medium text-zinc-500">Closed tickets</div>
|
||||
<div class="mt-3 text-4xl font-semibold text-emerald-700 dark:text-emerald-300">{{ $closedCount }}</div>
|
||||
<div class="mt-3 h-2 rounded-full bg-zinc-100 dark:bg-zinc-800"><div class="h-2 rounded-full bg-emerald-500" style="width: {{ $closedPercent }}%"></div></div>
|
||||
<div class="mt-3 h-2 rounded-full bg-zinc-100 dark:bg-zinc-800">
|
||||
<div class="h-2 rounded-full bg-emerald-500" style="width: {{ $closedPercent }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="text-sm font-medium text-zinc-500">High priority</div>
|
||||
@@ -23,7 +29,8 @@
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-3">
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900 lg:col-span-2">
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900 lg:col-span-2">
|
||||
<div class="mb-5 flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-zinc-900 dark:text-white">Performance</h2>
|
||||
@@ -33,12 +40,18 @@
|
||||
</div>
|
||||
<div class="space-y-5">
|
||||
<div>
|
||||
<div class="mb-2 flex justify-between text-sm"><span>Closed</span><span>{{ $closedPercent }}%</span></div>
|
||||
<div class="h-4 rounded-full bg-zinc-100 dark:bg-zinc-800"><div class="h-4 rounded-full bg-emerald-500" style="width: {{ $closedPercent }}%"></div></div>
|
||||
<div class="mb-2 flex justify-between text-sm"><span>Closed</span><span>{{ $closedPercent }}%</span>
|
||||
</div>
|
||||
<div class="h-4 rounded-full bg-zinc-100 dark:bg-zinc-800">
|
||||
<div class="h-4 rounded-full bg-emerald-500" style="width: {{ $closedPercent }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-2 flex justify-between text-sm"><span>Open</span><span>{{ $openPercent }}%</span></div>
|
||||
<div class="h-4 rounded-full bg-zinc-100 dark:bg-zinc-800"><div class="h-4 rounded-full bg-blue-500" style="width: {{ $openPercent }}%"></div></div>
|
||||
<div class="mb-2 flex justify-between text-sm"><span>Open</span><span>{{ $openPercent }}%</span>
|
||||
</div>
|
||||
<div class="h-4 rounded-full bg-zinc-100 dark:bg-zinc-800">
|
||||
<div class="h-4 rounded-full bg-blue-500" style="width: {{ $openPercent }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -46,16 +59,21 @@
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="mb-5 flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold text-zinc-900 dark:text-white">Recent commits</h2>
|
||||
<a href="{{ route('helpdesk.settings') }}" wire:navigate class="hd-btn hd-btn-sm hd-btn-violet">Git setup</a>
|
||||
<a href="{{ route('helpdesk.settings') }}" wire:navigate class="hd-btn hd-btn-sm hd-btn-violet">Git
|
||||
setup</a>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
@forelse ($recentCommits as $commit)
|
||||
<div class="rounded-xl border border-zinc-100 p-3 dark:border-zinc-800">
|
||||
<div class="font-mono text-xs text-zinc-500">{{ $commit->short_hash }} · {{ $commit->repository?->name }}</div>
|
||||
<div class="mt-1 truncate text-sm font-medium text-zinc-900 dark:text-zinc-100">{{ $commit->message }}</div>
|
||||
<div class="font-mono text-xs text-zinc-500">{{ $commit->short_hash }} ·
|
||||
{{ $commit->repository?->name }}</div>
|
||||
<div class="mt-1 truncate text-sm font-medium text-zinc-900 dark:text-zinc-100">
|
||||
{{ $commit->message }}</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="rounded-xl border border-dashed border-zinc-300 p-4 text-center text-sm text-zinc-500 dark:border-zinc-700">No commits tracked yet.</div>
|
||||
<div
|
||||
class="rounded-xl border border-dashed border-zinc-300 p-4 text-center text-sm text-zinc-500 dark:border-zinc-700">
|
||||
No commits tracked yet.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
@@ -67,19 +85,26 @@
|
||||
<h2 class="text-lg font-semibold text-zinc-900 dark:text-white">Recent tickets</h2>
|
||||
<p class="text-sm text-zinc-500 dark:text-zinc-400">Latest support activity.</p>
|
||||
</div>
|
||||
<a href="{{ route('helpdesk.tickets.index') }}" wire:navigate class="hd-btn hd-btn-sm hd-btn-blue">View all</a>
|
||||
<a href="{{ route('helpdesk.tickets.index') }}" wire:navigate class="hd-btn hd-btn-sm hd-btn-blue">View
|
||||
all</a>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
@forelse ($recentTickets as $ticket)
|
||||
<a href="{{ route('helpdesk.tickets.show', $ticket) }}" wire:navigate class="block rounded-xl border border-zinc-100 p-3 hover:bg-zinc-50 dark:border-zinc-800 dark:hover:bg-zinc-800">
|
||||
<a href="{{ route('helpdesk.tickets.show', $ticket) }}" wire:navigate
|
||||
class="block rounded-xl border border-zinc-100 p-3 hover:bg-zinc-50 dark:border-zinc-800 dark:hover:bg-zinc-800">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<span class="truncate text-sm font-medium text-zinc-900 dark:text-zinc-100">{{ $ticket->subject }}</span>
|
||||
<flux:badge color="{{ $ticket->priority->color }}" size="sm">{{ $ticket->priority->name }}</flux:badge>
|
||||
<span
|
||||
class="truncate text-sm font-medium text-zinc-900 dark:text-zinc-100">{{ $ticket->subject }}</span>
|
||||
<flux:badge color="{{ $ticket->priority->color }}" size="sm">
|
||||
{{ $ticket->priority->name }}</flux:badge>
|
||||
</div>
|
||||
<div class="mt-1 text-xs text-zinc-500">{{ $ticket->reference }} · {{ $ticket->status->name }}
|
||||
</div>
|
||||
<div class="mt-1 text-xs text-zinc-500">{{ $ticket->reference }} · {{ $ticket->status->name }}</div>
|
||||
</a>
|
||||
@empty
|
||||
<div class="rounded-xl border border-dashed border-zinc-300 p-4 text-center text-sm text-zinc-500 dark:border-zinc-700">No helpdesk tickets yet.</div>
|
||||
<div
|
||||
class="rounded-xl border border-dashed border-zinc-300 p-4 text-center text-sm text-zinc-500 dark:border-zinc-700">
|
||||
No helpdesk tickets yet.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
|
||||
@vite(['resources/css/app.css'])
|
||||
|
||||
<div class="hd-helpdesk-surface mx-auto max-w-[1800px] space-y-6" dusk="helpdesk-kanban-board" wire:poll.visible.5s="refreshBoard" wire:key="helpdesk-kanban-root-{{ $board_id ?? 0 }}-{{ $refreshNonce }}">
|
||||
<div class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="hd-helpdesk-surface mx-auto max-w-[1800px] space-y-6" dusk="helpdesk-kanban-board"
|
||||
wire:poll.visible.5s="refreshBoard" wire:key="helpdesk-kanban-root-{{ $board_id ?? 0 }}-{{ $refreshNonce }}">
|
||||
<div
|
||||
class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="bg-gradient-to-br from-slate-950 via-slate-900 to-blue-950 px-6 py-7 text-white">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">Helpdesk workflow</p>
|
||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight">Kanban board</h1>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">Drag tickets between board columns. Boards and columns are configured in Helpdesk Settings.</p>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">Drag tickets between board columns. Boards and
|
||||
columns are configured in Helpdesk Settings.</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@@ -36,162 +38,198 @@
|
||||
</div>
|
||||
</div>
|
||||
@if ($boards->isEmpty())
|
||||
<div class="rounded-2xl border border-amber-300 bg-amber-50 px-5 py-4 text-sm text-amber-900 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-100">
|
||||
<div
|
||||
class="rounded-2xl border border-amber-300 bg-amber-50 px-5 py-4 text-sm text-amber-900 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-100">
|
||||
No Kanban boards exist yet. Open Helpdesk Settings and create a board with at least one status column.
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col gap-4 rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-700 dark:bg-zinc-900 md:flex-row md:items-center md:justify-between">
|
||||
<div
|
||||
class="flex flex-col gap-4 rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-700 dark:bg-zinc-900 md:flex-row md:items-center md:justify-between">
|
||||
<div class="min-w-0">
|
||||
<h2 class="text-sm font-semibold text-zinc-900 dark:text-zinc-100">Active board</h2>
|
||||
<p class="text-xs text-zinc-500 dark:text-zinc-400">{{ $board?->description ?: 'Use settings to configure which statuses appear as draggable columns.' }}</p>
|
||||
<p class="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{{ $board?->description ?: 'Use settings to configure which statuses appear as draggable columns.' }}
|
||||
</p>
|
||||
@if ($board?->owner)
|
||||
<p class="mt-1 text-xs font-semibold text-emerald-600 dark:text-emerald-300">Owner: {{ $board->owner->name }}</p>
|
||||
<p class="mt-1 text-xs font-semibold text-emerald-600 dark:text-emerald-300">Owner:
|
||||
{{ $board->owner->name }}</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="hd-select-wrap shrink-0 md:w-72">
|
||||
<select wire:model.live="board_id" class="hd-select" aria-label="Select Kanban board">
|
||||
@foreach ($boards as $availableBoard)
|
||||
<option value="{{ $availableBoard->id }}">{{ $availableBoard->name }}{{ $availableBoard->owner ? ' · '.$availableBoard->owner->name : '' }}</option>
|
||||
<option value="{{ $availableBoard->id }}">
|
||||
{{ $availableBoard->name }}{{ $availableBoard->owner ? ' · ' . $availableBoard->owner->name : '' }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($statuses->isEmpty())
|
||||
<div class="rounded-2xl border border-amber-500/40 bg-amber-950/70 px-5 py-4 text-sm font-semibold text-amber-100">
|
||||
This board is being initialised with the default workflow statuses. Refresh the page if the columns do not appear immediately.
|
||||
<div
|
||||
class="rounded-2xl border border-amber-500/40 bg-amber-950/70 px-5 py-4 text-sm font-semibold text-amber-100">
|
||||
This board is being initialised with the default workflow statuses. Refresh the page if the columns do
|
||||
not appear immediately.
|
||||
</div>
|
||||
@else
|
||||
<div class="flex gap-4 overflow-x-auto pb-4" data-helpdesk-kanban-board wire:key="helpdesk-kanban-columns-{{ $board?->id ?? 0 }}-{{ $refreshNonce }}">
|
||||
@foreach ($statuses as $status)
|
||||
@php($tickets = $ticketsByStatus[$status->id] ?? collect())
|
||||
<div class="flex gap-4 overflow-x-auto pb-4" data-helpdesk-kanban-board
|
||||
wire:key="helpdesk-kanban-columns-{{ $board?->id ?? 0 }}-{{ $refreshNonce }}">
|
||||
@foreach ($statuses as $status)
|
||||
@php($tickets = $ticketsByStatus[$status->id] ?? collect())
|
||||
|
||||
<section class="flex min-h-[640px] w-[330px] shrink-0 flex-col overflow-hidden rounded-2xl border border-zinc-200 bg-zinc-100 shadow-sm dark:border-zinc-700 dark:bg-zinc-950" dusk="kanban-column-{{ $status->slug }}" wire:key="kanban-column-{{ $board?->id ?? 0 }}-{{ $status->id }}-{{ $refreshNonce }}">
|
||||
<header class="border-b border-zinc-200 bg-white px-4 py-4 dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<h2 class="text-sm font-bold uppercase tracking-wide text-zinc-900 dark:text-zinc-100">{{ $status->name }}</h2>
|
||||
<p class="mt-1 text-xs text-zinc-500"><span data-helpdesk-kanban-count data-status-id="{{ $status->id }}">{{ $tickets->count() }}</span> tickets</p>
|
||||
<section
|
||||
class="flex min-h-[640px] w-[330px] shrink-0 flex-col overflow-hidden rounded-2xl border border-zinc-200 bg-zinc-100 shadow-sm dark:border-zinc-700 dark:bg-zinc-950"
|
||||
dusk="kanban-column-{{ $status->slug }}"
|
||||
wire:key="kanban-column-{{ $board?->id ?? 0 }}-{{ $status->id }}-{{ $refreshNonce }}">
|
||||
<header
|
||||
class="border-b border-zinc-200 bg-white px-4 py-4 dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<h2
|
||||
class="text-sm font-bold uppercase tracking-wide text-zinc-900 dark:text-zinc-100">
|
||||
{{ $status->name }}</h2>
|
||||
<p class="mt-1 text-xs text-zinc-500"><span data-helpdesk-kanban-count
|
||||
data-status-id="{{ $status->id }}">{{ $tickets->count() }}</span>
|
||||
tickets</p>
|
||||
</div>
|
||||
<span class="inline-flex h-4 w-4 rounded-full ring-4 ring-zinc-100 dark:ring-zinc-800"
|
||||
style="background-color: {{ \Modules\Helpdesk\Support\Color::css($status->color) }}"></span>
|
||||
</div>
|
||||
<span class="inline-flex h-4 w-4 rounded-full ring-4 ring-zinc-100 dark:ring-zinc-800" style="background-color: {{ \Modules\Helpdesk\Support\Color::css($status->color) }}"></span>
|
||||
</div>
|
||||
</header>
|
||||
</header>
|
||||
|
||||
<div
|
||||
id="helpdesk-kanban-column-{{ $status->id }}"
|
||||
class="helpdesk-kanban-column min-h-[560px] flex-1 space-y-3 p-3 transition data-[drag-over=true]:bg-blue-50 dark:data-[drag-over=true]:bg-blue-950/30"
|
||||
data-helpdesk-kanban-column
|
||||
data-status-id="{{ $status->id }}"
|
||||
wire:key="kanban-dropzone-{{ $board?->id ?? 0 }}-{{ $status->id }}-{{ $refreshNonce }}"
|
||||
>
|
||||
@forelse ($tickets as $ticket)
|
||||
<article
|
||||
class="group cursor-grab rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm transition hover:-translate-y-0.5 hover:shadow-md active:cursor-grabbing dark:border-zinc-800 dark:bg-zinc-900"
|
||||
draggable="true"
|
||||
data-helpdesk-kanban-ticket
|
||||
data-ticket-id="{{ $ticket->id }}"
|
||||
wire:key="kanban-ticket-{{ $ticket->id }}-{{ $ticket->status_id }}-{{ $ticket->board_id }}-{{ optional($ticket->updated_at)->timestamp }}"
|
||||
dusk="kanban-ticket-{{ $ticket->id }}"
|
||||
>
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<div class="font-mono text-xs font-semibold text-blue-700 dark:text-blue-300">{{ $ticket->reference }}</div>
|
||||
<h3 class="mt-1 line-clamp-3 text-sm font-semibold leading-5 text-zinc-900 dark:text-zinc-100">{{ $ticket->subject }}</h3>
|
||||
<div id="helpdesk-kanban-column-{{ $status->id }}"
|
||||
class="helpdesk-kanban-column min-h-[560px] flex-1 space-y-3 p-3 transition data-[drag-over=true]:bg-blue-50 dark:data-[drag-over=true]:bg-blue-950/30"
|
||||
data-helpdesk-kanban-column data-status-id="{{ $status->id }}"
|
||||
wire:key="kanban-dropzone-{{ $board?->id ?? 0 }}-{{ $status->id }}-{{ $refreshNonce }}">
|
||||
@forelse ($tickets as $ticket)
|
||||
<article
|
||||
class="group cursor-grab rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm transition hover:-translate-y-0.5 hover:shadow-md active:cursor-grabbing dark:border-zinc-800 dark:bg-zinc-900"
|
||||
draggable="true" data-helpdesk-kanban-ticket data-ticket-id="{{ $ticket->id }}"
|
||||
wire:key="kanban-ticket-{{ $ticket->id }}-{{ $ticket->status_id }}-{{ $ticket->board_id }}-{{ optional($ticket->updated_at)->timestamp }}"
|
||||
dusk="kanban-ticket-{{ $ticket->id }}">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<div
|
||||
class="font-mono text-xs font-semibold text-blue-700 dark:text-blue-300">
|
||||
{{ $ticket->reference }}</div>
|
||||
<h3
|
||||
class="mt-1 line-clamp-3 text-sm font-semibold leading-5 text-zinc-900 dark:text-zinc-100">
|
||||
{{ $ticket->subject }}</h3>
|
||||
</div>
|
||||
|
||||
@if ($ticket->priority)
|
||||
<span class="shrink-0 rounded-full px-2 py-1 text-xs font-bold text-white"
|
||||
style="background-color: {{ \Modules\Helpdesk\Support\Color::css($ticket->priority->color) }}">
|
||||
{{ $ticket->priority->name }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if ($ticket->priority)
|
||||
<span class="shrink-0 rounded-full px-2 py-1 text-xs font-bold text-white" style="background-color: {{ \Modules\Helpdesk\Support\Color::css($ticket->priority->color) }}">
|
||||
{{ $ticket->priority->name }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="mt-3 space-y-1.5 text-xs text-zinc-500 dark:text-zinc-400">
|
||||
<div class="flex items-center gap-2"><i class="fa-solid fa-user w-3" aria-hidden="true"></i> {{ $ticket->owner?->name ?? 'Unknown' }}</div>
|
||||
<div class="flex items-center gap-2"><i class="fa-solid fa-user-gear w-3" aria-hidden="true"></i> {{ $ticket->agent?->name ?? 'Unassigned' }}</div>
|
||||
@if ($ticket->project)
|
||||
<div class="flex items-center gap-2"><i class="fa-solid fa-diagram-project w-3" aria-hidden="true"></i> {{ $ticket->project->title }}</div>
|
||||
@endif
|
||||
@if ($ticket->team)
|
||||
<div class="flex items-center gap-2"><i class="fa-solid fa-users w-3" aria-hidden="true"></i> {{ $ticket->team->name }}</div>
|
||||
@endif
|
||||
@if ($ticket->product)
|
||||
<div class="flex items-center gap-2"><i class="fa-solid fa-box w-3" aria-hidden="true"></i> {{ $ticket->product->name }}</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="mt-3 space-y-1.5 text-xs text-zinc-500 dark:text-zinc-400">
|
||||
<div class="flex items-center gap-2"><i class="fa-solid fa-user w-3"
|
||||
aria-hidden="true"></i> {{ $ticket->owner?->name ?? 'Unknown' }}</div>
|
||||
<div class="flex items-center gap-2"><i class="fa-solid fa-user-gear w-3"
|
||||
aria-hidden="true"></i> {{ $ticket->agent?->name ?? 'Unassigned' }}
|
||||
</div>
|
||||
@if ($ticket->project)
|
||||
<div class="flex items-center gap-2"><i
|
||||
class="fa-solid fa-diagram-project w-3" aria-hidden="true"></i>
|
||||
{{ $ticket->project->title }}</div>
|
||||
@endif
|
||||
@if ($ticket->team)
|
||||
<div class="flex items-center gap-2"><i class="fa-solid fa-users w-3"
|
||||
aria-hidden="true"></i> {{ $ticket->team->name }}</div>
|
||||
@endif
|
||||
@if ($ticket->product)
|
||||
<div class="flex items-center gap-2"><i class="fa-solid fa-box w-3"
|
||||
aria-hidden="true"></i> {{ $ticket->product->name }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mt-3 rounded-xl border border-emerald-500/20 bg-emerald-950/20 p-2">
|
||||
<label class="mb-1 block text-[11px] font-bold uppercase tracking-wide text-emerald-200/80">Board</label>
|
||||
<div class="hd-ticket-board-select-wrap">
|
||||
<select
|
||||
class="hd-ticket-board-select"
|
||||
wire:change="moveTicketToBoard({{ $ticket->id }}, $event.target.value)"
|
||||
aria-label="Move {{ $ticket->reference }} to another board"
|
||||
>
|
||||
@foreach ($boards as $ticketBoard)
|
||||
<option value="{{ $ticketBoard->id }}" @selected((int) $ticket->board_id === (int) $ticketBoard->id)>{{ $ticketBoard->name }}</option>
|
||||
<div class="mt-3 rounded-xl border border-emerald-500/20 bg-emerald-950/20 p-2">
|
||||
<label
|
||||
class="mb-1 block text-[11px] font-bold uppercase tracking-wide text-emerald-200/80">Board</label>
|
||||
<div class="hd-ticket-board-select-wrap">
|
||||
<select class="hd-ticket-board-select"
|
||||
wire:change="moveTicketToBoard({{ $ticket->id }}, $event.target.value)"
|
||||
aria-label="Move {{ $ticket->reference }} to another board">
|
||||
@foreach ($boards as $ticketBoard)
|
||||
<option value="{{ $ticketBoard->id }}"
|
||||
@selected((int) $ticket->board_id === (int) $ticketBoard->id)>{{ $ticketBoard->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if ($ticket->tags->isNotEmpty())
|
||||
<div class="mt-3 flex flex-wrap gap-1">
|
||||
@foreach ($ticket->tags as $tag)
|
||||
<span
|
||||
class="rounded-full px-2 py-0.5 text-[11px] font-semibold text-white"
|
||||
style="background-color: {{ \Modules\Helpdesk\Support\Color::css($tag->color) }}">{{ $tag->name }}</span>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($ticket->checklistItems->isNotEmpty())
|
||||
@php($done = $ticket->checklistItems->where('is_completed', true)->count())
|
||||
<div class="mt-3 text-xs text-zinc-500">Checklist
|
||||
{{ $done }}/{{ $ticket->checklistItems->count() }}</div>
|
||||
@endif
|
||||
|
||||
@if ($ticket->sla_first_response_breached || $ticket->sla_resolution_breached)
|
||||
<div
|
||||
class="mt-3 rounded-lg border border-red-300 bg-red-50 px-3 py-2 text-xs font-semibold text-red-800 dark:border-red-900 dark:bg-red-950 dark:text-red-100">
|
||||
SLA breach</div>
|
||||
@endif
|
||||
|
||||
<div
|
||||
class="mt-4 flex items-center justify-between gap-2 border-t border-zinc-100 pt-3 dark:border-zinc-800">
|
||||
<a href="{{ route('helpdesk.tickets.show', $ticket) }}" wire:navigate
|
||||
class="rounded-lg bg-blue-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-blue-700">Open</a>
|
||||
@can('update', $ticket)
|
||||
<a href="{{ route('helpdesk.tickets.edit', $ticket) }}" wire:navigate
|
||||
class="rounded-lg bg-emerald-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-emerald-700">Edit</a>
|
||||
@endcan
|
||||
</div>
|
||||
</article>
|
||||
@empty
|
||||
<div
|
||||
class="rounded-2xl border border-dashed border-zinc-300 bg-white p-6 text-center text-sm text-zinc-500 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
Drop tickets here for {{ $status->name }}.
|
||||
</div>
|
||||
@if ($ticket->tags->isNotEmpty())
|
||||
<div class="mt-3 flex flex-wrap gap-1">
|
||||
@foreach ($ticket->tags as $tag)
|
||||
<span class="rounded-full px-2 py-0.5 text-[11px] font-semibold text-white" style="background-color: {{ \Modules\Helpdesk\Support\Color::css($tag->color) }}">{{ $tag->name }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($ticket->checklistItems->isNotEmpty())
|
||||
@php($done = $ticket->checklistItems->where('is_completed', true)->count())
|
||||
<div class="mt-3 text-xs text-zinc-500">Checklist {{ $done }}/{{ $ticket->checklistItems->count() }}</div>
|
||||
@endif
|
||||
|
||||
@if ($ticket->sla_first_response_breached || $ticket->sla_resolution_breached)
|
||||
<div class="mt-3 rounded-lg border border-red-300 bg-red-50 px-3 py-2 text-xs font-semibold text-red-800 dark:border-red-900 dark:bg-red-950 dark:text-red-100">SLA breach</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-4 flex items-center justify-between gap-2 border-t border-zinc-100 pt-3 dark:border-zinc-800">
|
||||
<a href="{{ route('helpdesk.tickets.show', $ticket) }}" wire:navigate class="rounded-lg bg-blue-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-blue-700">Open</a>
|
||||
@can('update', $ticket)
|
||||
<a href="{{ route('helpdesk.tickets.edit', $ticket) }}" wire:navigate class="rounded-lg bg-emerald-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-emerald-700">Edit</a>
|
||||
@endcan
|
||||
</div>
|
||||
</article>
|
||||
@empty
|
||||
<div class="rounded-2xl border border-dashed border-zinc-300 bg-white p-6 text-center text-sm text-zinc-500 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
Drop tickets here for {{ $status->name }}.
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</section>
|
||||
@endforeach
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</section>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@script
|
||||
<script>
|
||||
const initialiseHelpdeskKanban = function () {
|
||||
const initialiseHelpdeskKanban = function() {
|
||||
let draggedTicketId = null;
|
||||
let draggedCard = null;
|
||||
|
||||
const updateColumnCounts = function () {
|
||||
document.querySelectorAll('[data-helpdesk-kanban-count]').forEach(function (counter) {
|
||||
const updateColumnCounts = function() {
|
||||
document.querySelectorAll('[data-helpdesk-kanban-count]').forEach(function(counter) {
|
||||
const statusId = counter.dataset.statusId;
|
||||
const column = document.querySelector('[data-helpdesk-kanban-column][data-status-id="' + statusId + '"]');
|
||||
const column = document.querySelector('[data-helpdesk-kanban-column][data-status-id="' +
|
||||
statusId + '"]');
|
||||
|
||||
if (!column) {
|
||||
return;
|
||||
}
|
||||
|
||||
counter.textContent = column.querySelectorAll('[data-helpdesk-kanban-ticket]').length.toString();
|
||||
counter.textContent = column.querySelectorAll('[data-helpdesk-kanban-ticket]').length
|
||||
.toString();
|
||||
});
|
||||
};
|
||||
|
||||
const forceLivewireRefresh = function () {
|
||||
const forceLivewireRefresh = function() {
|
||||
if (typeof $wire !== 'undefined' && typeof $wire.$refresh === 'function') {
|
||||
return Promise.resolve($wire.$refresh());
|
||||
}
|
||||
@@ -210,11 +248,11 @@
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
document.querySelectorAll('[data-helpdesk-kanban-ticket]').forEach(function (card) {
|
||||
document.querySelectorAll('[data-helpdesk-kanban-ticket]').forEach(function(card) {
|
||||
if (card.dataset.nativeDragReady === '1') return;
|
||||
card.dataset.nativeDragReady = '1';
|
||||
|
||||
card.addEventListener('dragstart', function (event) {
|
||||
card.addEventListener('dragstart', function(event) {
|
||||
draggedTicketId = card.dataset.ticketId;
|
||||
draggedCard = card;
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
@@ -223,38 +261,41 @@
|
||||
card.classList.add('opacity-60');
|
||||
});
|
||||
|
||||
card.addEventListener('dragend', function () {
|
||||
card.addEventListener('dragend', function() {
|
||||
card.classList.remove('opacity-60');
|
||||
document.querySelectorAll('[data-helpdesk-kanban-column]').forEach(function (column) {
|
||||
document.querySelectorAll('[data-helpdesk-kanban-column]').forEach(function(
|
||||
column) {
|
||||
column.dataset.dragOver = 'false';
|
||||
column.dataset.refreshing = 'false';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-helpdesk-kanban-column]').forEach(function (column) {
|
||||
document.querySelectorAll('[data-helpdesk-kanban-column]').forEach(function(column) {
|
||||
if (column.dataset.nativeDropReady === '1') return;
|
||||
column.dataset.nativeDropReady = '1';
|
||||
|
||||
column.addEventListener('dragover', function (event) {
|
||||
column.addEventListener('dragover', function(event) {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
column.dataset.dragOver = 'true';
|
||||
});
|
||||
|
||||
column.addEventListener('dragleave', function (event) {
|
||||
column.addEventListener('dragleave', function(event) {
|
||||
if (!column.contains(event.relatedTarget)) {
|
||||
column.dataset.dragOver = 'false';
|
||||
}
|
||||
});
|
||||
|
||||
column.addEventListener('drop', function (event) {
|
||||
column.addEventListener('drop', function(event) {
|
||||
event.preventDefault();
|
||||
column.dataset.dragOver = 'false';
|
||||
|
||||
const ticketId = event.dataTransfer.getData('application/x-helpdesk-ticket-id') || event.dataTransfer.getData('text/plain') || draggedTicketId;
|
||||
const ticketId = event.dataTransfer.getData('application/x-helpdesk-ticket-id') ||
|
||||
event.dataTransfer.getData('text/plain') || draggedTicketId;
|
||||
const statusId = column.dataset.statusId;
|
||||
const card = draggedCard || document.querySelector('[data-helpdesk-kanban-ticket][data-ticket-id="' + ticketId + '"]');
|
||||
const card = draggedCard || document.querySelector(
|
||||
'[data-helpdesk-kanban-ticket][data-ticket-id="' + ticketId + '"]');
|
||||
|
||||
if (!ticketId || !statusId || !card) return;
|
||||
|
||||
@@ -264,7 +305,7 @@
|
||||
|
||||
Promise.resolve($wire.moveTicket(ticketId, statusId))
|
||||
.then(forceLivewireRefresh)
|
||||
.finally(function () {
|
||||
.finally(function() {
|
||||
column.dataset.refreshing = 'false';
|
||||
draggedTicketId = null;
|
||||
draggedCard = null;
|
||||
@@ -280,8 +321,8 @@
|
||||
initialiseHelpdeskKanban();
|
||||
document.addEventListener('livewire:navigated', initialiseHelpdeskKanban);
|
||||
document.addEventListener('livewire:updated', initialiseHelpdeskKanban);
|
||||
document.addEventListener('helpdesk-kanban-moved', function () {
|
||||
setTimeout(function () {
|
||||
document.addEventListener('helpdesk-kanban-moved', function() {
|
||||
setTimeout(function() {
|
||||
initialiseHelpdeskKanban();
|
||||
|
||||
if (typeof $wire !== 'undefined' && typeof $wire.$refresh === 'function') {
|
||||
|
||||
@@ -21,13 +21,16 @@
|
||||
@endphp
|
||||
|
||||
<div class="hd-helpdesk-surface space-y-6" dusk="helpdesk-settings-manager">
|
||||
<div class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="bg-gradient-to-br from-slate-950 via-slate-900 to-blue-950 px-6 py-7 text-white">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">Helpdesk administration</p>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">Helpdesk administration
|
||||
</p>
|
||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight">Support configuration</h1>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">Manage lookup data, workflow messages, canned responses, SLA policies, automation rules, checklists, tags and product/service linkage.</p>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">Manage lookup data, workflow messages, canned
|
||||
responses, SLA policies, automation rules, checklists, tags and product/service linkage.</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@@ -41,22 +44,22 @@
|
||||
</div>
|
||||
<div class="grid gap-3 md:grid-cols-4 xl:grid-cols-7">
|
||||
@foreach ([
|
||||
'statuses' => ['label' => 'Statuses', 'count' => $statuses->count()],
|
||||
'priorities' => ['label' => 'Priorities', 'count' => $priorities->count()],
|
||||
'categories' => ['label' => 'Categories', 'count' => $categories->count()],
|
||||
'teams' => ['label' => 'Teams', 'count' => $teams->count()],
|
||||
'projects' => ['label' => 'Projects', 'count' => $projects->count()],
|
||||
'boards' => ['label' => 'Boards', 'count' => $boards->count()],
|
||||
'git' => ['label' => 'Git', 'count' => $repositories->count()],
|
||||
'workflow' => ['label' => 'Workflow', 'count' => $workflowTemplates->count()],
|
||||
'canned' => ['label' => 'Canned', 'count' => $cannedResponses->count()],
|
||||
'sla' => ['label' => 'SLA', 'count' => $slaPolicies->count()],
|
||||
'automation' => ['label' => 'Rules', 'count' => $automationRules->count()],
|
||||
'checklists' => ['label' => 'Checks', 'count' => $checklistTemplates->count()],
|
||||
'tags' => ['label' => 'Tags', 'count' => $tags->count()],
|
||||
'products' => ['label' => 'Products', 'count' => $products->count()],
|
||||
'defaults' => ['label' => 'Defaults', 'count' => 1],
|
||||
] as $key => $item)
|
||||
'statuses' => ['label' => 'Statuses', 'count' => $statuses->count()],
|
||||
'priorities' => ['label' => 'Priorities', 'count' => $priorities->count()],
|
||||
'categories' => ['label' => 'Categories', 'count' => $categories->count()],
|
||||
'teams' => ['label' => 'Teams', 'count' => $teams->count()],
|
||||
'projects' => ['label' => 'Projects', 'count' => $projects->count()],
|
||||
'boards' => ['label' => 'Boards', 'count' => $boards->count()],
|
||||
'git' => ['label' => 'Git', 'count' => $repositories->count()],
|
||||
'workflow' => ['label' => 'Workflow', 'count' => $workflowTemplates->count()],
|
||||
'canned' => ['label' => 'Canned', 'count' => $cannedResponses->count()],
|
||||
'sla' => ['label' => 'SLA', 'count' => $slaPolicies->count()],
|
||||
'automation' => ['label' => 'Rules', 'count' => $automationRules->count()],
|
||||
'checklists' => ['label' => 'Checks', 'count' => $checklistTemplates->count()],
|
||||
'tags' => ['label' => 'Tags', 'count' => $tags->count()],
|
||||
'products' => ['label' => 'Products', 'count' => $products->count()],
|
||||
'defaults' => ['label' => 'Defaults', 'count' => 1],
|
||||
] as $key => $item)
|
||||
@php
|
||||
$sectionPalette = match ($key) {
|
||||
'statuses' => ['border' => '#38bdf8', 'bg' => 'rgba(14, 165, 233, .22)', 'active' => '#0284c7'],
|
||||
@@ -77,11 +80,23 @@
|
||||
default => ['border' => '#71717a', 'bg' => 'rgba(63, 63, 70, .22)', 'active' => '#3f3f46'],
|
||||
};
|
||||
|
||||
$sectionStyle = $section === $key
|
||||
? 'border-color: '.$sectionPalette['border'].'; background-color: '.$sectionPalette['active'].'; color: #ffffff; box-shadow: 0 16px 36px rgba(0,0,0,.32);'
|
||||
: 'border-color: '.$sectionPalette['border'].'; background-color: '.$sectionPalette['bg'].'; color: #ffffff; box-shadow: 0 10px 24px rgba(0,0,0,.20);';
|
||||
$sectionStyle =
|
||||
$section === $key
|
||||
? 'border-color: ' .
|
||||
$sectionPalette['border'] .
|
||||
'; background-color: ' .
|
||||
$sectionPalette['active'] .
|
||||
'; color: #ffffff; box-shadow: 0 16px 36px rgba(0,0,0,.32);'
|
||||
: 'border-color: ' .
|
||||
$sectionPalette['border'] .
|
||||
'; background-color: ' .
|
||||
$sectionPalette['bg'] .
|
||||
'; color: #ffffff; box-shadow: 0 10px 24px rgba(0,0,0,.20);';
|
||||
@endphp
|
||||
<a href="{{ route('helpdesk.settings', ['section' => $key]) }}" dusk="helpdesk-settings-section-{{ $key }}" class="rounded-2xl border px-4 py-3 text-left shadow-sm transition hover:-translate-y-0.5" style="{{ $sectionStyle }}">
|
||||
<a href="{{ route('helpdesk.settings', ['section' => $key]) }}"
|
||||
dusk="helpdesk-settings-section-{{ $key }}"
|
||||
class="rounded-2xl border px-4 py-3 text-left shadow-sm transition hover:-translate-y-0.5"
|
||||
style="{{ $sectionStyle }}">
|
||||
<div class="text-xs font-semibold uppercase tracking-wide opacity-70">{{ $item['label'] }}</div>
|
||||
<div class="mt-1 text-2xl font-semibold">{{ $item['count'] }}</div>
|
||||
</a>
|
||||
@@ -90,23 +105,21 @@
|
||||
|
||||
|
||||
@if ($section === 'defaults')
|
||||
<x-helpdesk::settings-section title="Ticket defaults" description="Choose the company default used when a new Helpdesk ticket is created. Lookup defaults are configured on the Statuses, Priorities, Categories, Teams and Projects sections.">
|
||||
<x-helpdesk::settings-section title="Ticket defaults"
|
||||
description="Choose the company default used when a new Helpdesk ticket is created. Lookup defaults are configured on the Statuses, Priorities, Categories, Teams and Projects sections.">
|
||||
<x-slot:form>
|
||||
<form class="grid gap-4 md:grid-cols-3">
|
||||
<flux:select wire:model="default_helpdesk_company_id" label="Default company">
|
||||
<option value="">No default company</option>
|
||||
@foreach ($companies as $company)
|
||||
<option value="{{ $company->id }}">
|
||||
{{ $company->name }}{{ ($company->is_default_helpdesk_company ?? false) ? ' — Default' : '' }}
|
||||
{{ $company->name }}{{ $company->is_default_helpdesk_company ?? false ? ' — Default' : '' }}
|
||||
</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
|
||||
<div class="md:col-span-3 flex gap-2">
|
||||
<flux:button
|
||||
type="button"
|
||||
wire:click="saveDefaultHelpdeskCompany"
|
||||
variant="primary"
|
||||
<flux:button type="button" wire:click="saveDefaultHelpdeskCompany" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">
|
||||
Save default company
|
||||
</flux:button>
|
||||
@@ -114,27 +127,41 @@
|
||||
</form>
|
||||
</x-slot:form>
|
||||
|
||||
<div class="rounded-2xl border border-zinc-200 bg-zinc-50 p-4 text-sm text-zinc-700 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-200">
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-zinc-50 p-4 text-sm text-zinc-700 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-200">
|
||||
<p class="font-semibold">Lookup defaults</p>
|
||||
<p class="mt-1">Set the default Status, Priority, Category, Team and Project from their own settings sections. Only one default per company/global scope is kept for each lookup type.</p>
|
||||
<p class="mt-1">Set the default Status, Priority, Category, Team and Project from their own settings
|
||||
sections. Only one default per company/global scope is kept for each lookup type.</p>
|
||||
</div>
|
||||
</x-helpdesk::settings-section>
|
||||
@endif
|
||||
|
||||
@if ($section === 'statuses')
|
||||
<x-helpdesk::settings-section title="Statuses" description="Control workflow stages and mark which statuses close tickets.">
|
||||
<x-helpdesk::settings-section title="Statuses"
|
||||
description="Control workflow stages and mark which statuses close tickets.">
|
||||
<x-slot:form>
|
||||
<form wire:submit.prevent="saveStatus" class="grid gap-4 md:grid-cols-5">
|
||||
<flux:input wire:model="statusForm.name" label="Name" />
|
||||
<flux:input wire:model="statusForm.slug" label="Slug" placeholder="auto-generated" />
|
||||
<flux:input wire:model="statusForm.color" label="Badge colour" />
|
||||
<flux:input wire:model="statusForm.sort_order" label="Sort" type="number" min="0" />
|
||||
<label class="flex items-end gap-2 pb-2 text-sm text-zinc-700 dark:text-zinc-200"><input type="checkbox" wire:model="statusForm.is_closed" class="rounded border-zinc-300"> Closed status</label>
|
||||
<label class="md:col-span-5 flex items-center gap-2 text-sm font-semibold text-zinc-700 dark:text-zinc-200">
|
||||
<input type="checkbox" wire:model="statusForm.is_default" class="rounded border-zinc-300 bg-zinc-950 text-blue-600 focus:ring-blue-500">
|
||||
<label class="flex items-end gap-2 pb-2 text-sm text-zinc-700 dark:text-zinc-200"><input
|
||||
type="checkbox" wire:model="statusForm.is_closed" class="rounded border-zinc-300"> Closed
|
||||
status</label>
|
||||
<label
|
||||
class="md:col-span-5 flex items-center gap-2 text-sm font-semibold text-zinc-700 dark:text-zinc-200">
|
||||
<input type="checkbox" wire:model="statusForm.is_default"
|
||||
class="rounded border-zinc-300 bg-zinc-950 text-blue-600 focus:ring-blue-500">
|
||||
Default status for new tickets
|
||||
</label>
|
||||
<div class="md:col-span-5 flex gap-2"><flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save status</flux:button><flux:button type="button" wire:click="resetStatusForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button></div>
|
||||
<div class="md:col-span-5 flex gap-2">
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save status
|
||||
</flux:button>
|
||||
<flux:button type="button" wire:click="resetStatusForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
<x-helpdesk::lookup-table :items="$statuses" edit="editStatus" delete="deleteStatus" />
|
||||
@@ -150,10 +177,18 @@
|
||||
<flux:input wire:model="priorityForm.color" label="Badge colour" />
|
||||
<flux:input wire:model="priorityForm.sort_order" label="Sort" type="number" min="0" />
|
||||
<label class="flex items-end gap-2 pb-2 text-sm font-semibold text-zinc-700 dark:text-zinc-200">
|
||||
<input type="checkbox" wire:model="priorityForm.is_default" class="rounded border-zinc-300 bg-zinc-950 text-blue-600 focus:ring-blue-500">
|
||||
<input type="checkbox" wire:model="priorityForm.is_default"
|
||||
class="rounded border-zinc-300 bg-zinc-950 text-blue-600 focus:ring-blue-500">
|
||||
Default for new tickets
|
||||
</label>
|
||||
<div class="md:col-span-4 flex gap-2"><flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save priority</flux:button><flux:button type="button" wire:click="resetPriorityForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button></div>
|
||||
<div class="md:col-span-4 flex gap-2">
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save priority
|
||||
</flux:button>
|
||||
<flux:button type="button" wire:click="resetPriorityForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
<x-helpdesk::lookup-table :items="$priorities" edit="editPriority" delete="deletePriority" />
|
||||
@@ -161,19 +196,36 @@
|
||||
@endif
|
||||
|
||||
@if ($section === 'categories')
|
||||
<x-helpdesk::settings-section title="Categories" description="Group tickets by support area, optionally per company.">
|
||||
<x-helpdesk::settings-section title="Categories"
|
||||
description="Group tickets by support area, optionally per company.">
|
||||
<x-slot:form>
|
||||
<form wire:submit.prevent="saveCategory" class="grid gap-4 md:grid-cols-4">
|
||||
<flux:select wire:model="categoryForm.company_id" label="Company"><option value="">Global</option>@foreach ($companies as $company)<option value="{{ $company->id }}">{{ $company->name }}</option>@endforeach</flux:select>
|
||||
<flux:select wire:model="categoryForm.company_id" label="Company">
|
||||
<option value="">Global</option>
|
||||
@foreach ($companies as $company)
|
||||
<option value="{{ $company->id }}">{{ $company->name }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<flux:input wire:model="categoryForm.name" label="Name" />
|
||||
<flux:input wire:model="categoryForm.slug" label="Slug" placeholder="auto-generated" />
|
||||
<flux:input wire:model="categoryForm.color" label="Badge colour" />
|
||||
<div class="md:col-span-4"><flux:textarea wire:model="categoryForm.description" label="Description" rows="3" /></div>
|
||||
<label class="md:col-span-4 flex items-center gap-2 text-sm font-semibold text-zinc-700 dark:text-zinc-200">
|
||||
<input type="checkbox" wire:model="categoryForm.is_default" class="rounded border-zinc-300 bg-zinc-950 text-blue-600 focus:ring-blue-500">
|
||||
<div class="md:col-span-4">
|
||||
<flux:textarea wire:model="categoryForm.description" label="Description" rows="3" />
|
||||
</div>
|
||||
<label
|
||||
class="md:col-span-4 flex items-center gap-2 text-sm font-semibold text-zinc-700 dark:text-zinc-200">
|
||||
<input type="checkbox" wire:model="categoryForm.is_default"
|
||||
class="rounded border-zinc-300 bg-zinc-950 text-blue-600 focus:ring-blue-500">
|
||||
Default category for new tickets
|
||||
</label>
|
||||
<div class="md:col-span-4 flex gap-2"><flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save category</flux:button><flux:button type="button" wire:click="resetCategoryForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button></div>
|
||||
<div class="md:col-span-4 flex gap-2">
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save
|
||||
category</flux:button>
|
||||
<flux:button type="button" wire:click="resetCategoryForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
<x-helpdesk::lookup-table :items="$categories" edit="editCategory" delete="deleteCategory" />
|
||||
@@ -181,19 +233,36 @@
|
||||
@endif
|
||||
|
||||
@if ($section === 'teams')
|
||||
<x-helpdesk::settings-section title="Teams" description="Create support teams under each company and assign projects to them.">
|
||||
<x-helpdesk::settings-section title="Teams"
|
||||
description="Create support teams under each company and assign projects to them.">
|
||||
<x-slot:form>
|
||||
<form wire:submit.prevent="saveTeam" class="grid gap-4 md:grid-cols-4">
|
||||
<flux:select wire:model="teamForm.company_id" label="Company"><option value="">Global</option>@foreach ($companies as $company)<option value="{{ $company->id }}">{{ $company->name }}</option>@endforeach</flux:select>
|
||||
<flux:select wire:model="teamForm.company_id" label="Company">
|
||||
<option value="">Global</option>
|
||||
@foreach ($companies as $company)
|
||||
<option value="{{ $company->id }}">{{ $company->name }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<flux:input wire:model="teamForm.name" label="Name" />
|
||||
<flux:input wire:model="teamForm.slug" label="Slug" placeholder="auto-generated" />
|
||||
<flux:input wire:model="teamForm.color" label="Badge colour" />
|
||||
<div class="md:col-span-4"><flux:textarea wire:model="teamForm.description" label="Description" rows="3" /></div>
|
||||
<label class="md:col-span-4 flex items-center gap-2 text-sm font-semibold text-zinc-700 dark:text-zinc-200">
|
||||
<input type="checkbox" wire:model="teamForm.is_default" class="rounded border-zinc-300 bg-zinc-950 text-blue-600 focus:ring-blue-500">
|
||||
<div class="md:col-span-4">
|
||||
<flux:textarea wire:model="teamForm.description" label="Description" rows="3" />
|
||||
</div>
|
||||
<label
|
||||
class="md:col-span-4 flex items-center gap-2 text-sm font-semibold text-zinc-700 dark:text-zinc-200">
|
||||
<input type="checkbox" wire:model="teamForm.is_default"
|
||||
class="rounded border-zinc-300 bg-zinc-950 text-blue-600 focus:ring-blue-500">
|
||||
Default team for new tickets
|
||||
</label>
|
||||
<div class="md:col-span-4 flex gap-2"><flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save team</flux:button><flux:button type="button" wire:click="resetTeamForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button></div>
|
||||
<div class="md:col-span-4 flex gap-2">
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save team
|
||||
</flux:button>
|
||||
<flux:button type="button" wire:click="resetTeamForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
<x-helpdesk::team-table :items="$teams" />
|
||||
@@ -201,20 +270,42 @@
|
||||
@endif
|
||||
|
||||
@if ($section === 'projects')
|
||||
<x-helpdesk::settings-section title="Projects" description="Create company projects and optionally assign them to support teams.">
|
||||
<x-helpdesk::settings-section title="Projects"
|
||||
description="Create company projects and optionally assign them to support teams.">
|
||||
<x-slot:form>
|
||||
<form wire:submit.prevent="saveProject" class="grid gap-4 md:grid-cols-5">
|
||||
<flux:select wire:model="projectForm.company_id" label="Company"><option value="">Global</option>@foreach ($companies as $company)<option value="{{ $company->id }}">{{ $company->name }}</option>@endforeach</flux:select>
|
||||
<flux:select wire:model="projectForm.team_id" label="Team"><option value="">No team</option>@foreach ($teams as $team)<option value="{{ $team->id }}">{{ $team->name }}</option>@endforeach</flux:select>
|
||||
<flux:select wire:model="projectForm.company_id" label="Company">
|
||||
<option value="">Global</option>
|
||||
@foreach ($companies as $company)
|
||||
<option value="{{ $company->id }}">{{ $company->name }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<flux:select wire:model="projectForm.team_id" label="Team">
|
||||
<option value="">No team</option>
|
||||
@foreach ($teams as $team)
|
||||
<option value="{{ $team->id }}">{{ $team->name }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<flux:input wire:model="projectForm.title" label="Title" />
|
||||
<flux:input wire:model="projectForm.slug" label="Slug" placeholder="auto-generated" />
|
||||
<flux:input wire:model="projectForm.color" label="Badge colour" />
|
||||
<div class="md:col-span-5"><flux:textarea wire:model="projectForm.description" label="Description" rows="3" /></div>
|
||||
<label class="md:col-span-5 flex items-center gap-2 text-sm font-semibold text-zinc-700 dark:text-zinc-200">
|
||||
<input type="checkbox" wire:model="projectForm.is_default" class="rounded border-zinc-300 bg-zinc-950 text-blue-600 focus:ring-blue-500">
|
||||
<div class="md:col-span-5">
|
||||
<flux:textarea wire:model="projectForm.description" label="Description" rows="3" />
|
||||
</div>
|
||||
<label
|
||||
class="md:col-span-5 flex items-center gap-2 text-sm font-semibold text-zinc-700 dark:text-zinc-200">
|
||||
<input type="checkbox" wire:model="projectForm.is_default"
|
||||
class="rounded border-zinc-300 bg-zinc-950 text-blue-600 focus:ring-blue-500">
|
||||
Default project for new tickets
|
||||
</label>
|
||||
<div class="md:col-span-5 flex gap-2"><flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save project</flux:button><flux:button type="button" wire:click="resetProjectForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button></div>
|
||||
<div class="md:col-span-5 flex gap-2">
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save project
|
||||
</flux:button>
|
||||
<flux:button type="button" wire:click="resetProjectForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
<x-helpdesk::project-table :items="$projects" />
|
||||
@@ -223,7 +314,8 @@
|
||||
|
||||
|
||||
@if ($section === 'boards')
|
||||
<x-helpdesk::settings-section title="Kanban Boards" description="Create configurable workflow boards and choose which statuses appear as draggable columns.">
|
||||
<x-helpdesk::settings-section title="Kanban Boards"
|
||||
description="Create configurable workflow boards and choose which statuses appear as draggable columns.">
|
||||
<x-slot:form>
|
||||
<form wire:submit.prevent="saveBoard" class="grid gap-4 md:grid-cols-7">
|
||||
<flux:input wire:model="boardForm.name" label="Name" />
|
||||
@@ -236,33 +328,44 @@
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<label class="flex items-end gap-2 pb-2 text-sm text-zinc-700 dark:text-zinc-200">
|
||||
<input type="checkbox" wire:model="boardForm.is_default" class="rounded border-zinc-300"> Default board
|
||||
<input type="checkbox" wire:model="boardForm.is_default" class="rounded border-zinc-300">
|
||||
Default board
|
||||
</label>
|
||||
<div class="md:col-span-2">
|
||||
<flux:textarea wire:model="boardForm.description" label="Description" rows="2" />
|
||||
</div>
|
||||
<div class="md:col-span-7">
|
||||
<label class="mb-2 block text-sm font-medium text-zinc-700 dark:text-zinc-200">Board columns</label>
|
||||
<label class="mb-2 block text-sm font-medium text-zinc-700 dark:text-zinc-200">Board
|
||||
columns</label>
|
||||
<div class="grid gap-2 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6">
|
||||
@foreach ($statuses as $status)
|
||||
<label class="flex items-center gap-2 rounded-xl border border-zinc-200 bg-white px-3 py-2 text-sm text-zinc-700 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-200">
|
||||
<input type="checkbox" wire:model="boardForm.status_ids" value="{{ $status->id }}" class="rounded border-zinc-300">
|
||||
<span class="inline-flex h-3 w-3 rounded-full" style="background-color: {{ \Modules\Helpdesk\Support\Color::css($status->color) }}"></span>
|
||||
<label
|
||||
class="flex items-center gap-2 rounded-xl border border-zinc-200 bg-white px-3 py-2 text-sm text-zinc-700 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-200">
|
||||
<input type="checkbox" wire:model="boardForm.status_ids"
|
||||
value="{{ $status->id }}" class="rounded border-zinc-300">
|
||||
<span class="inline-flex h-3 w-3 rounded-full"
|
||||
style="background-color: {{ \Modules\Helpdesk\Support\Color::css($status->color) }}"></span>
|
||||
<span>{{ $status->name }}</span>
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
<div class="md:col-span-7 flex gap-2">
|
||||
<flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save board</flux:button>
|
||||
<flux:button type="button" wire:click="resetBoardForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button>
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save board
|
||||
</flux:button>
|
||||
<flux:button type="button" wire:click="resetBoardForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
|
||||
<div class="overflow-hidden rounded-2xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
|
||||
<thead class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<thead
|
||||
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<tr>
|
||||
<th class="px-4 py-3">Board</th>
|
||||
<th class="px-4 py-3">Owner</th>
|
||||
@@ -276,26 +379,42 @@
|
||||
@forelse ($boards as $boardItem)
|
||||
<tr>
|
||||
<td class="px-4 py-3">
|
||||
<div class="font-medium text-zinc-900 dark:text-zinc-100">{{ $boardItem->name }}</div>
|
||||
<div class="font-medium text-zinc-900 dark:text-zinc-100">{{ $boardItem->name }}
|
||||
</div>
|
||||
<div class="text-xs text-zinc-500">{{ $boardItem->slug }}</div>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm text-zinc-600 dark:text-zinc-300">{{ $boardItem->owner?->name ?? 'Unassigned' }}</td>
|
||||
<td class="px-4 py-3 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
{{ $boardItem->owner?->name ?? 'Unassigned' }}</td>
|
||||
<td class="px-4 py-3">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
@foreach ($boardItem->statuses as $status)
|
||||
<span class="rounded-full px-2 py-1 text-xs font-semibold text-white" style="background-color: {{ \Modules\Helpdesk\Support\Color::css($status->color) }}">{{ $status->name }}</span>
|
||||
<span class="rounded-full px-2 py-1 text-xs font-semibold text-white"
|
||||
style="background-color: {{ \Modules\Helpdesk\Support\Color::css($status->color) }}">{{ $status->name }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-zinc-500">{{ $boardItem->tickets_count ?? 0 }}</td>
|
||||
<td class="px-4 py-3"><flux:badge color="{{ $boardItem->is_default ? 'emerald' : 'zinc' }}">{{ $boardItem->is_default ? 'Default' : 'No' }}</flux:badge></td>
|
||||
<td class="px-4 py-3">
|
||||
<flux:badge color="{{ $boardItem->is_default ? 'emerald' : 'zinc' }}">
|
||||
{{ $boardItem->is_default ? 'Default' : 'No' }}</flux:badge>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<flux:button size="sm" variant="primary" wire:click="editBoard({{ $boardItem->id }})" class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Edit</flux:button>
|
||||
<flux:button size="sm" variant="danger" wire:click="deleteBoard({{ $boardItem->id }})" wire:confirm="Delete this board?" class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete</flux:button>
|
||||
<flux:button size="sm" variant="primary"
|
||||
wire:click="editBoard({{ $boardItem->id }})"
|
||||
class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">
|
||||
Edit</flux:button>
|
||||
<flux:button size="sm" variant="danger"
|
||||
wire:click="deleteBoard({{ $boardItem->id }})"
|
||||
wire:confirm="Delete this board?"
|
||||
class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete
|
||||
</flux:button>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="px-4 py-8 text-center text-zinc-500">No Kanban boards yet.</td></tr>
|
||||
<tr>
|
||||
<td colspan="6" class="px-4 py-8 text-center text-zinc-500">No Kanban boards yet.
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -304,7 +423,8 @@
|
||||
@endif
|
||||
|
||||
@if ($section === 'git')
|
||||
<x-helpdesk::settings-section title="Git repositories" description="Configure repositories that can be linked to projects and tickets. Commits can then be stored against tickets for traceability.">
|
||||
<x-helpdesk::settings-section title="Git repositories"
|
||||
description="Configure repositories that can be linked to projects and tickets. Commits can then be stored against tickets for traceability.">
|
||||
<x-slot:form>
|
||||
<form wire:submit.prevent="saveRepository" class="grid gap-4 md:grid-cols-6">
|
||||
<flux:select wire:model="repositoryForm.company_id" label="Company">
|
||||
@@ -320,15 +440,21 @@
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<flux:input wire:model="repositoryForm.name" label="Name" />
|
||||
<flux:input wire:model="repositoryForm.provider" label="Provider" placeholder="git, github, gitlab" />
|
||||
<flux:input wire:model="repositoryForm.provider" label="Provider"
|
||||
placeholder="git, github, gitlab" />
|
||||
<flux:input wire:model="repositoryForm.default_branch" label="Default branch" />
|
||||
<flux:input wire:model="repositoryForm.webhook_secret" label="Webhook secret" />
|
||||
<div class="md:col-span-7">
|
||||
<flux:input wire:model="repositoryForm.remote_url" label="Remote URL" placeholder="git@github.com:company/repo.git" />
|
||||
<flux:input wire:model="repositoryForm.remote_url" label="Remote URL"
|
||||
placeholder="git@github.com:company/repo.git" />
|
||||
</div>
|
||||
<div class="md:col-span-7 flex gap-2">
|
||||
<flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save repository</flux:button>
|
||||
<flux:button type="button" wire:click="resetRepositoryForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button>
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save
|
||||
repository</flux:button>
|
||||
<flux:button type="button" wire:click="resetRepositoryForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
@@ -338,7 +464,8 @@
|
||||
|
||||
|
||||
@if ($section === 'workflow')
|
||||
<x-helpdesk::settings-section title="Workflow messages" description="Define the professional email messages sent when tickets are raised, updated, replied to and closed.">
|
||||
<x-helpdesk::settings-section title="Workflow messages"
|
||||
description="Define the professional email messages sent when tickets are raised, updated, replied to and closed.">
|
||||
<x-slot:form>
|
||||
<form wire:submit.prevent="saveWorkflowTemplate" class="grid gap-4 md:grid-cols-6">
|
||||
<flux:input wire:model="workflowForm.name" label="Name" />
|
||||
@@ -354,28 +481,41 @@
|
||||
<option value="agent">Assigned agent</option>
|
||||
<option value="actor">User making change</option>
|
||||
</flux:select>
|
||||
<flux:input wire:model="workflowForm.cc" label="CC" placeholder="Optional comma-separated emails" />
|
||||
<flux:input wire:model="workflowForm.bcc" label="BCC" placeholder="Optional comma-separated emails" />
|
||||
<label class="flex items-end gap-2 pb-2 text-sm text-zinc-700 dark:text-zinc-200"><input type="checkbox" wire:model="workflowForm.is_active" class="rounded border-zinc-300"> Active</label>
|
||||
<flux:input wire:model="workflowForm.cc" label="CC"
|
||||
placeholder="Optional comma-separated emails" />
|
||||
<flux:input wire:model="workflowForm.bcc" label="BCC"
|
||||
placeholder="Optional comma-separated emails" />
|
||||
<label class="flex items-end gap-2 pb-2 text-sm text-zinc-700 dark:text-zinc-200"><input
|
||||
type="checkbox" wire:model="workflowForm.is_active" class="rounded border-zinc-300">
|
||||
Active</label>
|
||||
<div class="md:col-span-7">
|
||||
<flux:input wire:model="workflowForm.subject" label="Subject" placeholder="Ticket @{{ ticket.reference }} has been raised" />
|
||||
<flux:input wire:model="workflowForm.subject" label="Subject"
|
||||
placeholder="Ticket @{{ ticket.reference }} has been raised" />
|
||||
</div>
|
||||
<div class="md:col-span-7">
|
||||
<flux:textarea wire:model="workflowForm.html_body" label="HTML email body" rows="8" placeholder="Use tokens like @{{ ticket.reference }}, @{{ ticket.subject }}, @{{ ticket.status }}, @{{ requester.name }}, @{{ ticket.url }}" />
|
||||
<flux:textarea wire:model="workflowForm.html_body" label="HTML email body" rows="8"
|
||||
placeholder="Use tokens like @{{ ticket.reference }}, @{{ ticket.subject }}, @{{ ticket.status }}, @{{ requester.name }}, @{{ ticket.url }}" />
|
||||
</div>
|
||||
<div class="md:col-span-7">
|
||||
<flux:textarea wire:model="workflowForm.text_body" label="Plain text body" rows="4" placeholder="Optional fallback text" />
|
||||
<flux:textarea wire:model="workflowForm.text_body" label="Plain text body" rows="4"
|
||||
placeholder="Optional fallback text" />
|
||||
</div>
|
||||
<div class="md:col-span-7 flex gap-2">
|
||||
<flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save workflow message</flux:button>
|
||||
<flux:button type="button" wire:click="resetWorkflowTemplateForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button>
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save
|
||||
workflow message</flux:button>
|
||||
<flux:button type="button" wire:click="resetWorkflowTemplateForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
|
||||
<div class="overflow-hidden rounded-2xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
|
||||
<thead class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<thead
|
||||
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<tr>
|
||||
<th class="px-4 py-3">Name</th>
|
||||
<th class="px-4 py-3">Event</th>
|
||||
@@ -387,17 +527,31 @@
|
||||
<tbody class="divide-y divide-zinc-100 dark:divide-zinc-800">
|
||||
@forelse ($workflowTemplates as $template)
|
||||
<tr>
|
||||
<td class="px-4 py-3 font-medium text-zinc-900 dark:text-zinc-100">{{ $template->name }}</td>
|
||||
<td class="px-4 py-3 font-medium text-zinc-900 dark:text-zinc-100">
|
||||
{{ $template->name }}</td>
|
||||
<td class="px-4 py-3 text-zinc-600 dark:text-zinc-300">{{ $template->event }}</td>
|
||||
<td class="px-4 py-3 text-zinc-600 dark:text-zinc-300">{{ $template->to_role }}</td>
|
||||
<td class="px-4 py-3"><flux:badge color="{{ $template->is_active ? 'emerald' : 'zinc' }}">{{ $template->is_active ? 'Active' : 'Inactive' }}</flux:badge></td>
|
||||
<td class="px-4 py-3">
|
||||
<flux:badge color="{{ $template->is_active ? 'emerald' : 'zinc' }}">
|
||||
{{ $template->is_active ? 'Active' : 'Inactive' }}</flux:badge>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<flux:button size="sm" variant="ghost" wire:click="editWorkflowTemplate({{ $template->id }})" class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Edit</flux:button>
|
||||
<flux:button size="sm" variant="danger" wire:click="deleteWorkflowTemplate({{ $template->id }})" wire:confirm="Delete this workflow message?" class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete</flux:button>
|
||||
<flux:button size="sm" variant="ghost"
|
||||
wire:click="editWorkflowTemplate({{ $template->id }})"
|
||||
class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">
|
||||
Edit</flux:button>
|
||||
<flux:button size="sm" variant="danger"
|
||||
wire:click="deleteWorkflowTemplate({{ $template->id }})"
|
||||
wire:confirm="Delete this workflow message?"
|
||||
class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete
|
||||
</flux:button>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="5" class="px-4 py-8 text-center text-zinc-500">No workflow messages yet.</td></tr>
|
||||
<tr>
|
||||
<td colspan="5" class="px-4 py-8 text-center text-zinc-500">No workflow messages
|
||||
yet.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -407,95 +561,394 @@
|
||||
|
||||
|
||||
@if ($section === 'canned')
|
||||
<x-helpdesk::settings-section title="Canned responses" description="Save professional reusable replies with macro tokens for ticket, requester, agent, status and URL values.">
|
||||
<x-helpdesk::settings-section title="Canned responses"
|
||||
description="Save professional reusable replies with macro tokens for ticket, requester, agent, status and URL values.">
|
||||
<x-slot:form>
|
||||
<form wire:submit.prevent="saveCannedResponse" class="grid gap-4 md:grid-cols-6">
|
||||
<flux:input wire:model="cannedForm.name" label="Name" />
|
||||
<flux:input wire:model="cannedForm.slug" label="Slug" placeholder="auto-generated" />
|
||||
<flux:input wire:model="cannedForm.sort_order" label="Sort" type="number" min="0" />
|
||||
<flux:select wire:model="cannedForm.category_id" label="Category"><option value="">Any category</option>@foreach ($categories as $category)<option value="{{ $category->id }}">{{ $category->name }}</option>@endforeach</flux:select>
|
||||
<flux:select wire:model="cannedForm.team_id" label="Team"><option value="">Any team</option>@foreach ($teams as $team)<option value="{{ $team->id }}">{{ $team->name }}</option>@endforeach</flux:select>
|
||||
<label class="flex items-end gap-2 pb-2 text-sm text-zinc-700 dark:text-zinc-200"><input type="checkbox" wire:model="cannedForm.is_active" class="rounded border-zinc-300"> Active</label>
|
||||
<div class="md:col-span-7"><flux:input wire:model="cannedForm.subject" label="Optional subject" /></div>
|
||||
<div class="md:col-span-7"><flux:textarea wire:model="cannedForm.body" label="Reply body" rows="6" placeholder="Hello @{{ requester.name }}, Your ticket @{{ ticket.reference }} has been reviewed." /></div>
|
||||
<div class="md:col-span-7 flex gap-2"><flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save canned response</flux:button><flux:button type="button" wire:click="resetCannedResponseForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button></div>
|
||||
<flux:select wire:model="cannedForm.category_id" label="Category">
|
||||
<option value="">Any category</option>
|
||||
@foreach ($categories as $category)
|
||||
<option value="{{ $category->id }}">{{ $category->name }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<flux:select wire:model="cannedForm.team_id" label="Team">
|
||||
<option value="">Any team</option>
|
||||
@foreach ($teams as $team)
|
||||
<option value="{{ $team->id }}">{{ $team->name }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<label class="flex items-end gap-2 pb-2 text-sm text-zinc-700 dark:text-zinc-200"><input
|
||||
type="checkbox" wire:model="cannedForm.is_active" class="rounded border-zinc-300">
|
||||
Active</label>
|
||||
<div class="md:col-span-7">
|
||||
<flux:input wire:model="cannedForm.subject" label="Optional subject" />
|
||||
</div>
|
||||
<div class="md:col-span-7">
|
||||
<flux:textarea wire:model="cannedForm.body" label="Reply body" rows="6"
|
||||
placeholder="Hello @{{ requester.name }}, Your ticket @{{ ticket.reference }} has been reviewed." />
|
||||
</div>
|
||||
<div class="md:col-span-7 flex gap-2">
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save canned
|
||||
response</flux:button>
|
||||
<flux:button type="button" wire:click="resetCannedResponseForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
||||
@forelse ($cannedResponses as $response)
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-4 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="flex items-start justify-between gap-3"><div><h3 class="font-semibold text-zinc-900 dark:text-zinc-100">{{ $response->name }}</h3><p class="text-xs text-zinc-500">{{ $response->category?->name ?? 'Any category' }} · {{ $response->team?->name ?? 'Any team' }}</p></div><flux:badge color="{{ $response->is_active ? 'emerald' : 'zinc' }}">{{ $response->is_active ? 'Active' : 'Inactive' }}</flux:badge></div>
|
||||
<p class="mt-3 line-clamp-3 text-sm text-zinc-600 dark:text-zinc-300">{{ strip_tags($response->body) }}</p>
|
||||
<div class="mt-4 flex justify-end gap-2"><flux:button size="sm" variant="primary" wire:click="editCannedResponse({{ $response->id }})" class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Edit</flux:button><flux:button size="sm" variant="danger" wire:click="deleteCannedResponse({{ $response->id }})" wire:confirm="Delete this canned response?" class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete</flux:button></div>
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<h3 class="font-semibold text-zinc-900 dark:text-zinc-100">{{ $response->name }}</h3>
|
||||
<p class="text-xs text-zinc-500">{{ $response->category?->name ?? 'Any category' }} ·
|
||||
{{ $response->team?->name ?? 'Any team' }}</p>
|
||||
</div>
|
||||
<flux:badge color="{{ $response->is_active ? 'emerald' : 'zinc' }}">
|
||||
{{ $response->is_active ? 'Active' : 'Inactive' }}</flux:badge>
|
||||
</div>
|
||||
<p class="mt-3 line-clamp-3 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
{{ strip_tags($response->body) }}</p>
|
||||
<div class="mt-4 flex justify-end gap-2">
|
||||
<flux:button size="sm" variant="primary"
|
||||
wire:click="editCannedResponse({{ $response->id }})"
|
||||
class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Edit
|
||||
</flux:button>
|
||||
<flux:button size="sm" variant="danger"
|
||||
wire:click="deleteCannedResponse({{ $response->id }})"
|
||||
wire:confirm="Delete this canned response?"
|
||||
class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="rounded-2xl border border-dashed border-zinc-300 p-8 text-center text-sm text-zinc-500 dark:border-zinc-700 md:col-span-2 xl:col-span-3">No canned responses yet.</div>
|
||||
<div
|
||||
class="rounded-2xl border border-dashed border-zinc-300 p-8 text-center text-sm text-zinc-500 dark:border-zinc-700 md:col-span-2 xl:col-span-3">
|
||||
No canned responses yet.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-helpdesk::settings-section>
|
||||
@endif
|
||||
|
||||
@if ($section === 'sla')
|
||||
<x-helpdesk::settings-section title="SLA policies" description="Define first response and resolution targets used for breach indicators, reporting and ticket cards.">
|
||||
<x-helpdesk::settings-section title="SLA policies"
|
||||
description="Define first response and resolution targets used for breach indicators, reporting and ticket cards.">
|
||||
<x-slot:form>
|
||||
<form wire:submit.prevent="saveSlaPolicy" class="grid gap-4 md:grid-cols-6">
|
||||
<flux:input wire:model="slaForm.name" label="Name" />
|
||||
<flux:input wire:model="slaForm.slug" label="Slug" placeholder="auto-generated" />
|
||||
<flux:select wire:model="slaForm.priority_id" label="Priority"><option value="">Any priority</option>@foreach ($priorities as $priority)<option value="{{ $priority->id }}">{{ $priority->name }}</option>@endforeach</flux:select>
|
||||
<flux:select wire:model="slaForm.category_id" label="Category"><option value="">Any category</option>@foreach ($categories as $category)<option value="{{ $category->id }}">{{ $category->name }}</option>@endforeach</flux:select>
|
||||
<flux:input wire:model="slaForm.first_response_minutes" label="First response minutes" type="number" min="1" />
|
||||
<flux:input wire:model="slaForm.resolution_minutes" label="Resolution minutes" type="number" min="1" />
|
||||
<label class="flex items-center gap-2 text-sm text-zinc-700 dark:text-zinc-200"><input type="checkbox" wire:model="slaForm.is_default" class="rounded border-zinc-300"> Default</label>
|
||||
<label class="flex items-center gap-2 text-sm text-zinc-700 dark:text-zinc-200"><input type="checkbox" wire:model="slaForm.business_hours_only" class="rounded border-zinc-300"> Business hours</label>
|
||||
<label class="flex items-center gap-2 text-sm text-zinc-700 dark:text-zinc-200"><input type="checkbox" wire:model="slaForm.is_active" class="rounded border-zinc-300"> Active</label>
|
||||
<div class="md:col-span-7 flex gap-2"><flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save SLA policy</flux:button><flux:button type="button" wire:click="resetSlaPolicyForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button></div>
|
||||
<flux:select wire:model="slaForm.priority_id" label="Priority">
|
||||
<option value="">Any priority</option>
|
||||
@foreach ($priorities as $priority)
|
||||
<option value="{{ $priority->id }}">{{ $priority->name }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<flux:select wire:model="slaForm.category_id" label="Category">
|
||||
<option value="">Any category</option>
|
||||
@foreach ($categories as $category)
|
||||
<option value="{{ $category->id }}">{{ $category->name }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<flux:input wire:model="slaForm.first_response_minutes" label="First response minutes"
|
||||
type="number" min="1" />
|
||||
<flux:input wire:model="slaForm.resolution_minutes" label="Resolution minutes" type="number"
|
||||
min="1" />
|
||||
<label class="flex items-center gap-2 text-sm text-zinc-700 dark:text-zinc-200"><input
|
||||
type="checkbox" wire:model="slaForm.is_default" class="rounded border-zinc-300">
|
||||
Default</label>
|
||||
<label class="flex items-center gap-2 text-sm text-zinc-700 dark:text-zinc-200"><input
|
||||
type="checkbox" wire:model="slaForm.business_hours_only" class="rounded border-zinc-300">
|
||||
Business hours</label>
|
||||
<label class="flex items-center gap-2 text-sm text-zinc-700 dark:text-zinc-200"><input
|
||||
type="checkbox" wire:model="slaForm.is_active" class="rounded border-zinc-300">
|
||||
Active</label>
|
||||
<div class="md:col-span-7 flex gap-2">
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save SLA
|
||||
policy</flux:button>
|
||||
<flux:button type="button" wire:click="resetSlaPolicyForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
<div class="overflow-hidden rounded-2xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900"><table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700"><thead class="bg-zinc-50 text-left text-xs font-semibold uppercase text-zinc-500 dark:bg-zinc-800"><tr><th class="px-4 py-3">Policy</th><th class="px-4 py-3">Targets</th><th class="px-4 py-3">Applies to</th><th class="px-4 py-3 text-right">Actions</th></tr></thead><tbody class="divide-y divide-zinc-100 dark:divide-zinc-800">@forelse ($slaPolicies as $policy)<tr><td class="px-4 py-3 font-medium">{{ $policy->name }} @if($policy->is_default)<flux:badge color="blue">Default</flux:badge>@endif</td><td class="px-4 py-3">{{ $policy->first_response_minutes }}m response · {{ $policy->resolution_minutes }}m close</td><td class="px-4 py-3">{{ $policy->priority?->name ?? 'Any priority' }} · {{ $policy->category?->name ?? 'Any category' }}</td><td class="px-4 py-3 text-right"><flux:button size="sm" variant="primary" wire:click="editSlaPolicy({{ $policy->id }})" class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Edit</flux:button> <flux:button size="sm" variant="danger" wire:click="deleteSlaPolicy({{ $policy->id }})" wire:confirm="Delete this SLA policy?" class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete</flux:button></td></tr>@empty<tr><td colspan="6" class="px-4 py-8 text-center text-zinc-500">No SLA policies yet.</td></tr>@endforelse</tbody></table></div>
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
|
||||
<thead class="bg-zinc-50 text-left text-xs font-semibold uppercase text-zinc-500 dark:bg-zinc-800">
|
||||
<tr>
|
||||
<th class="px-4 py-3">Policy</th>
|
||||
<th class="px-4 py-3">Targets</th>
|
||||
<th class="px-4 py-3">Applies to</th>
|
||||
<th class="px-4 py-3 text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-zinc-100 dark:divide-zinc-800">
|
||||
@forelse ($slaPolicies as $policy)
|
||||
<tr>
|
||||
<td class="px-4 py-3 font-medium">{{ $policy->name }} @if ($policy->is_default)
|
||||
<flux:badge color="blue">Default</flux:badge>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-4 py-3">{{ $policy->first_response_minutes }}m response ·
|
||||
{{ $policy->resolution_minutes }}m close</td>
|
||||
<td class="px-4 py-3">{{ $policy->priority?->name ?? 'Any priority' }} ·
|
||||
{{ $policy->category?->name ?? 'Any category' }}</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<flux:button size="sm" variant="primary"
|
||||
wire:click="editSlaPolicy({{ $policy->id }})"
|
||||
class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">
|
||||
Edit</flux:button>
|
||||
<flux:button size="sm" variant="danger"
|
||||
wire:click="deleteSlaPolicy({{ $policy->id }})"
|
||||
wire:confirm="Delete this SLA policy?"
|
||||
class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete
|
||||
</flux:button>
|
||||
</td>
|
||||
</tr>@empty<tr>
|
||||
<td colspan="6" class="px-4 py-8 text-center text-zinc-500">No SLA policies yet.
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-helpdesk::settings-section>
|
||||
@endif
|
||||
|
||||
@if ($section === 'automation')
|
||||
<x-helpdesk::settings-section title="Email automation rules" description="Match incoming support messages and automatically assign priority, category, team, project, agent and status.">
|
||||
<x-helpdesk::settings-section title="Email automation rules"
|
||||
description="Match incoming support messages and automatically assign priority, category, team, project, agent and status.">
|
||||
<x-slot:form>
|
||||
<form wire:submit.prevent="saveAutomationRule" class="grid gap-4 md:grid-cols-6">
|
||||
<flux:input wire:model="automationForm.name" label="Name" />
|
||||
<flux:input wire:model="automationForm.slug" label="Slug" placeholder="auto-generated" />
|
||||
<flux:input wire:model="automationForm.sort_order" label="Sort" type="number" min="0" />
|
||||
<flux:input wire:model="automationForm.sort_order" label="Sort" type="number"
|
||||
min="0" />
|
||||
<flux:input wire:model="automationForm.match_sender" label="Sender contains" />
|
||||
<flux:input wire:model="automationForm.match_subject" label="Subject contains" />
|
||||
<flux:input wire:model="automationForm.match_mailbox" label="Mailbox contains" />
|
||||
<flux:select wire:model="automationForm.set_priority_id" label="Set priority"><option value="">No change</option>@foreach ($priorities as $priority)<option value="{{ $priority->id }}">{{ $priority->name }}</option>@endforeach</flux:select>
|
||||
<flux:select wire:model="automationForm.set_category_id" label="Set category"><option value="">No change</option>@foreach ($categories as $category)<option value="{{ $category->id }}">{{ $category->name }}</option>@endforeach</flux:select>
|
||||
<flux:select wire:model="automationForm.set_team_id" label="Set team"><option value="">No change</option>@foreach ($teams as $team)<option value="{{ $team->id }}">{{ $team->name }}</option>@endforeach</flux:select>
|
||||
<flux:select wire:model="automationForm.set_project_id" label="Set project"><option value="">No change</option>@foreach ($projects as $project)<option value="{{ $project->id }}">{{ $project->title }}</option>@endforeach</flux:select>
|
||||
<flux:select wire:model="automationForm.set_status_id" label="Set status"><option value="">No change</option>@foreach ($statuses as $status)<option value="{{ $status->id }}">{{ $status->name }}</option>@endforeach</flux:select>
|
||||
<label class="flex items-end gap-2 pb-2 text-sm text-zinc-700 dark:text-zinc-200"><input type="checkbox" wire:model="automationForm.is_active" class="rounded border-zinc-300"> Active</label>
|
||||
<div class="md:col-span-7 flex gap-2"><flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save automation rule</flux:button><flux:button type="button" wire:click="resetAutomationRuleForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button></div>
|
||||
<flux:select wire:model="automationForm.set_priority_id" label="Set priority">
|
||||
<option value="">No change</option>
|
||||
@foreach ($priorities as $priority)
|
||||
<option value="{{ $priority->id }}">{{ $priority->name }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<flux:select wire:model="automationForm.set_category_id" label="Set category">
|
||||
<option value="">No change</option>
|
||||
@foreach ($categories as $category)
|
||||
<option value="{{ $category->id }}">{{ $category->name }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<flux:select wire:model="automationForm.set_team_id" label="Set team">
|
||||
<option value="">No change</option>
|
||||
@foreach ($teams as $team)
|
||||
<option value="{{ $team->id }}">{{ $team->name }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<flux:select wire:model="automationForm.set_project_id" label="Set project">
|
||||
<option value="">No change</option>
|
||||
@foreach ($projects as $project)
|
||||
<option value="{{ $project->id }}">{{ $project->title }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<flux:select wire:model="automationForm.set_status_id" label="Set status">
|
||||
<option value="">No change</option>
|
||||
@foreach ($statuses as $status)
|
||||
<option value="{{ $status->id }}">{{ $status->name }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<label class="flex items-end gap-2 pb-2 text-sm text-zinc-700 dark:text-zinc-200"><input
|
||||
type="checkbox" wire:model="automationForm.is_active" class="rounded border-zinc-300">
|
||||
Active</label>
|
||||
<div class="md:col-span-7 flex gap-2">
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save
|
||||
automation rule</flux:button>
|
||||
<flux:button type="button" wire:click="resetAutomationRuleForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
<div class="grid gap-3 md:grid-cols-2">@forelse ($automationRules as $rule)<div class="rounded-2xl border border-zinc-200 bg-white p-4 dark:border-zinc-700 dark:bg-zinc-900"><h3 class="font-semibold">{{ $rule->name }}</h3><p class="mt-1 text-sm text-zinc-500">Sender: {{ $rule->match_sender ?: 'Any' }} · Subject: {{ $rule->match_subject ?: 'Any' }}</p><div class="mt-4 flex justify-end gap-2"><flux:button size="sm" variant="primary" wire:click="editAutomationRule({{ $rule->id }})" class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Edit</flux:button><flux:button size="sm" variant="danger" wire:click="deleteAutomationRule({{ $rule->id }})" wire:confirm="Delete this rule?" class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete</flux:button></div></div>@empty<div class="rounded-2xl border border-dashed border-zinc-300 p-8 text-center text-sm text-zinc-500 dark:border-zinc-700 md:col-span-2">No automation rules yet.</div>@endforelse</div>
|
||||
<div class="grid gap-3 md:grid-cols-2">
|
||||
@forelse ($automationRules as $rule)
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-4 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<h3 class="font-semibold">{{ $rule->name }}</h3>
|
||||
<p class="mt-1 text-sm text-zinc-500">Sender: {{ $rule->match_sender ?: 'Any' }} · Subject:
|
||||
{{ $rule->match_subject ?: 'Any' }}</p>
|
||||
<div class="mt-4 flex justify-end gap-2">
|
||||
<flux:button size="sm" variant="primary"
|
||||
wire:click="editAutomationRule({{ $rule->id }})"
|
||||
class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Edit
|
||||
</flux:button>
|
||||
<flux:button size="sm" variant="danger"
|
||||
wire:click="deleteAutomationRule({{ $rule->id }})"
|
||||
wire:confirm="Delete this rule?"
|
||||
class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>@empty<div
|
||||
class="rounded-2xl border border-dashed border-zinc-300 p-8 text-center text-sm text-zinc-500 dark:border-zinc-700 md:col-span-2">
|
||||
No automation rules yet.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-helpdesk::settings-section>
|
||||
@endif
|
||||
|
||||
@if ($section === 'checklists')
|
||||
<x-helpdesk::settings-section title="Checklist templates" description="Apply repeatable checklist items by category or project and show completion progress on tickets and Kanban cards.">
|
||||
<x-slot:form><form wire:submit.prevent="saveChecklistTemplate" class="grid gap-4 md:grid-cols-6"><flux:input wire:model="checklistTemplateForm.name" label="Name" /><flux:input wire:model="checklistTemplateForm.slug" label="Slug" /><flux:select wire:model="checklistTemplateForm.category_id" label="Category"><option value="">Any category</option>@foreach ($categories as $category)<option value="{{ $category->id }}">{{ $category->name }}</option>@endforeach</flux:select><flux:select wire:model="checklistTemplateForm.project_id" label="Project"><option value="">Any project</option>@foreach ($projects as $project)<option value="{{ $project->id }}">{{ $project->title }}</option>@endforeach</flux:select><label class="flex items-end gap-2 pb-2 text-sm"><input type="checkbox" wire:model="checklistTemplateForm.is_active" class="rounded border-zinc-300"> Active</label><div class="md:col-span-7"><flux:textarea wire:model="checklistTemplateForm.items_text" label="Checklist items, one per line" rows="6" /></div><div class="md:col-span-7 flex gap-2"><flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save checklist</flux:button><flux:button type="button" wire:click="resetChecklistTemplateForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button></div></form></x-slot:form>
|
||||
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-3">@forelse ($checklistTemplates as $template)<div class="rounded-2xl border border-zinc-200 bg-white p-4 dark:border-zinc-700 dark:bg-zinc-900"><h3 class="font-semibold">{{ $template->name }}</h3><p class="mt-1 text-sm text-zinc-500">{{ count($template->items ?? []) }} items</p><div class="mt-4 flex justify-end gap-2"><flux:button size="sm" variant="primary" wire:click="editChecklistTemplate({{ $template->id }})" class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Edit</flux:button><flux:button size="sm" variant="danger" wire:click="deleteChecklistTemplate({{ $template->id }})" wire:confirm="Delete this checklist?" class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete</flux:button></div></div>@empty<div class="rounded-2xl border border-dashed border-zinc-300 p-8 text-center text-sm text-zinc-500 dark:border-zinc-700 md:col-span-2 xl:col-span-3">No checklist templates yet.</div>@endforelse</div>
|
||||
<x-helpdesk::settings-section title="Checklist templates"
|
||||
description="Apply repeatable checklist items by category or project and show completion progress on tickets and Kanban cards.">
|
||||
<x-slot:form>
|
||||
<form wire:submit.prevent="saveChecklistTemplate" class="grid gap-4 md:grid-cols-6">
|
||||
<flux:input wire:model="checklistTemplateForm.name" label="Name" />
|
||||
<flux:input wire:model="checklistTemplateForm.slug" label="Slug" />
|
||||
<flux:select wire:model="checklistTemplateForm.category_id" label="Category">
|
||||
<option value="">Any category</option>
|
||||
@foreach ($categories as $category)
|
||||
<option value="{{ $category->id }}">{{ $category->name }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<flux:select wire:model="checklistTemplateForm.project_id" label="Project">
|
||||
<option value="">Any project</option>
|
||||
@foreach ($projects as $project)
|
||||
<option value="{{ $project->id }}">{{ $project->title }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<label class="flex items-end gap-2 pb-2 text-sm"><input type="checkbox"
|
||||
wire:model="checklistTemplateForm.is_active" class="rounded border-zinc-300">
|
||||
Active</label>
|
||||
<div class="md:col-span-7">
|
||||
<flux:textarea wire:model="checklistTemplateForm.items_text"
|
||||
label="Checklist items, one per line" rows="6" />
|
||||
</div>
|
||||
<div class="md:col-span-7 flex gap-2">
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save
|
||||
checklist</flux:button>
|
||||
<flux:button type="button" wire:click="resetChecklistTemplateForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
||||
@forelse ($checklistTemplates as $template)
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-4 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<h3 class="font-semibold">{{ $template->name }}</h3>
|
||||
<p class="mt-1 text-sm text-zinc-500">{{ count($template->items ?? []) }} items</p>
|
||||
<div class="mt-4 flex justify-end gap-2">
|
||||
<flux:button size="sm" variant="primary"
|
||||
wire:click="editChecklistTemplate({{ $template->id }})"
|
||||
class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Edit
|
||||
</flux:button>
|
||||
<flux:button size="sm" variant="danger"
|
||||
wire:click="deleteChecklistTemplate({{ $template->id }})"
|
||||
wire:confirm="Delete this checklist?"
|
||||
class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>@empty<div
|
||||
class="rounded-2xl border border-dashed border-zinc-300 p-8 text-center text-sm text-zinc-500 dark:border-zinc-700 md:col-span-2 xl:col-span-3">
|
||||
No checklist templates yet.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-helpdesk::settings-section>
|
||||
@endif
|
||||
|
||||
@if ($section === 'tags')
|
||||
<x-helpdesk::settings-section title="Ticket tags" description="Maintain visible ticket labels for search, triage and Kanban card context.">
|
||||
<x-slot:form><form wire:submit.prevent="saveTag" class="grid gap-4 md:grid-cols-5"><flux:input wire:model="tagForm.name" label="Name" /><flux:input wire:model="tagForm.slug" label="Slug" /><flux:input wire:model="tagForm.color" label="Colour" /><div class="md:col-span-5 flex gap-2"><flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save tag</flux:button><flux:button type="button" wire:click="resetTagForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button></div></form></x-slot:form>
|
||||
<div class="flex flex-wrap gap-3">@forelse ($tags as $tag)<div class="rounded-2xl border border-zinc-200 bg-white px-4 py-3 dark:border-zinc-700 dark:bg-zinc-900"><span class="rounded-full px-2 py-1 text-xs font-semibold text-white" style="background-color: {{ \Modules\Helpdesk\Support\Color::css($tag->color) }}">{{ $tag->name }}</span><div class="mt-3 flex gap-2"><flux:button size="sm" variant="primary" wire:click="editTag({{ $tag->id }})" class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Edit</flux:button><flux:button size="sm" variant="danger" wire:click="deleteTag({{ $tag->id }})" wire:confirm="Delete this tag?" class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete</flux:button></div></div>@empty<div class="rounded-2xl border border-dashed border-zinc-300 p-8 text-center text-sm text-zinc-500 dark:border-zinc-700">No tags yet.</div>@endforelse</div>
|
||||
<x-helpdesk::settings-section title="Ticket tags"
|
||||
description="Maintain visible ticket labels for search, triage and Kanban card context.">
|
||||
<x-slot:form>
|
||||
<form wire:submit.prevent="saveTag" class="grid gap-4 md:grid-cols-5">
|
||||
<flux:input wire:model="tagForm.name" label="Name" />
|
||||
<flux:input wire:model="tagForm.slug" label="Slug" />
|
||||
<flux:input wire:model="tagForm.color" label="Colour" />
|
||||
<div class="md:col-span-5 flex gap-2">
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save tag
|
||||
</flux:button>
|
||||
<flux:button type="button" wire:click="resetTagForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
@forelse ($tags as $tag)
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white px-4 py-3 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<span class="rounded-full px-2 py-1 text-xs font-semibold text-white"
|
||||
style="background-color: {{ \Modules\Helpdesk\Support\Color::css($tag->color) }}">{{ $tag->name }}</span>
|
||||
<div class="mt-3 flex gap-2">
|
||||
<flux:button size="sm" variant="primary" wire:click="editTag({{ $tag->id }})"
|
||||
class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Edit
|
||||
</flux:button>
|
||||
<flux:button size="sm" variant="danger" wire:click="deleteTag({{ $tag->id }})"
|
||||
wire:confirm="Delete this tag?"
|
||||
class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>@empty<div
|
||||
class="rounded-2xl border border-dashed border-zinc-300 p-8 text-center text-sm text-zinc-500 dark:border-zinc-700">
|
||||
No tags yet.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-helpdesk::settings-section>
|
||||
@endif
|
||||
|
||||
@if ($section === 'products')
|
||||
<x-helpdesk::settings-section title="Products and services" description="Link tickets to products, services, releases or builds and report support cost/time by product.">
|
||||
<x-slot:form><form wire:submit.prevent="saveProduct" class="grid gap-4 md:grid-cols-6"><flux:input wire:model="productForm.name" label="Name" /><flux:input wire:model="productForm.slug" label="Slug" /><flux:input wire:model="productForm.type" label="Type" /><flux:input wire:model="productForm.version" label="Version" /><flux:input wire:model="productForm.release_channel" label="Release/build" /><label class="flex items-end gap-2 pb-2 text-sm"><input type="checkbox" wire:model="productForm.is_active" class="rounded border-zinc-300"> Active</label><div class="md:col-span-7"><flux:textarea wire:model="productForm.description" label="Description" rows="3" /></div><div class="md:col-span-7 flex gap-2"><flux:button type="submit" variant="primary" class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save product/service</flux:button><flux:button type="button" wire:click="resetProductForm" variant="ghost" class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset</flux:button></div></form></x-slot:form>
|
||||
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-3">@forelse ($products as $product)<div class="rounded-2xl border border-zinc-200 bg-white p-4 dark:border-zinc-700 dark:bg-zinc-900"><h3 class="font-semibold">{{ $product->name }}</h3><p class="mt-1 text-sm text-zinc-500">{{ ucfirst($product->type) }} · {{ $product->version ?: 'No version' }} · {{ $product->tickets_count }} tickets</p><div class="mt-4 flex justify-end gap-2"><flux:button size="sm" variant="primary" wire:click="editProduct({{ $product->id }})" class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Edit</flux:button><flux:button size="sm" variant="danger" wire:click="deleteProduct({{ $product->id }})" wire:confirm="Delete this product/service?" class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete</flux:button></div></div>@empty<div class="rounded-2xl border border-dashed border-zinc-300 p-8 text-center text-sm text-zinc-500 dark:border-zinc-700 md:col-span-2 xl:col-span-3">No products or services yet.</div>@endforelse</div>
|
||||
<x-helpdesk::settings-section title="Products and services"
|
||||
description="Link tickets to products, services, releases or builds and report support cost/time by product.">
|
||||
<x-slot:form>
|
||||
<form wire:submit.prevent="saveProduct" class="grid gap-4 md:grid-cols-6">
|
||||
<flux:input wire:model="productForm.name" label="Name" />
|
||||
<flux:input wire:model="productForm.slug" label="Slug" />
|
||||
<flux:input wire:model="productForm.type" label="Type" />
|
||||
<flux:input wire:model="productForm.version" label="Version" />
|
||||
<flux:input wire:model="productForm.release_channel" label="Release/build" /><label
|
||||
class="flex items-end gap-2 pb-2 text-sm"><input type="checkbox"
|
||||
wire:model="productForm.is_active" class="rounded border-zinc-300"> Active</label>
|
||||
<div class="md:col-span-7">
|
||||
<flux:textarea wire:model="productForm.description" label="Description" rows="3" />
|
||||
</div>
|
||||
<div class="md:col-span-7 flex gap-2">
|
||||
<flux:button type="submit" variant="primary"
|
||||
class="!bg-green-600 !text-white hover:!bg-green-700 focus:!ring-green-400/60">Save
|
||||
product/service</flux:button>
|
||||
<flux:button type="button" wire:click="resetProductForm" variant="ghost"
|
||||
class="!bg-amber-500 !text-white hover:!bg-amber-600 focus:!ring-amber-300/60">Reset
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</x-slot:form>
|
||||
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
||||
@forelse ($products as $product)
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-4 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<h3 class="font-semibold">{{ $product->name }}</h3>
|
||||
<p class="mt-1 text-sm text-zinc-500">{{ ucfirst($product->type) }} ·
|
||||
{{ $product->version ?: 'No version' }} · {{ $product->tickets_count }} tickets</p>
|
||||
<div class="mt-4 flex justify-end gap-2">
|
||||
<flux:button size="sm" variant="primary"
|
||||
wire:click="editProduct({{ $product->id }})"
|
||||
class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Edit
|
||||
</flux:button>
|
||||
<flux:button size="sm" variant="danger"
|
||||
wire:click="deleteProduct({{ $product->id }})"
|
||||
wire:confirm="Delete this product/service?"
|
||||
class="!bg-red-600 !text-white hover:!bg-red-700 focus:!ring-red-400/60">Delete
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>@empty<div
|
||||
class="rounded-2xl border border-dashed border-zinc-300 p-8 text-center text-sm text-zinc-500 dark:border-zinc-700 md:col-span-2 xl:col-span-3">
|
||||
No products or services yet.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-helpdesk::settings-section>
|
||||
@endif
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<div class="hd-helpdesk-surface space-y-6" dusk="helpdesk-ticket-create">
|
||||
@vite([
|
||||
'resources/css/app.css',
|
||||
'resources/js/app.js',
|
||||
])
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
@include('helpdesk::partials.page-header', [
|
||||
'eyebrow' => 'Helpdesk',
|
||||
'title' => 'Create ticket',
|
||||
'subtitle' => 'Raise a new support request with priority, product/service, project, category, team and full details.',
|
||||
'subtitle' =>
|
||||
'Raise a new support request with priority, product/service, project, category, team and full details.',
|
||||
])
|
||||
|
||||
<form wire:submit.prevent="save" enctype="multipart/form-data" class="helpdesk-ticket-form space-y-6">
|
||||
@@ -25,46 +23,35 @@
|
||||
Subject <span class="helpdesk-required-star">*</span>
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="subject"
|
||||
type="text"
|
||||
maxlength="180"
|
||||
wire:model.defer="subject"
|
||||
<input id="subject" type="text" maxlength="180" wire:model.defer="subject"
|
||||
class="mt-2 block h-14 w-full max-w-4xl rounded-xl border border-zinc-700 bg-zinc-950 px-4 text-base font-medium text-zinc-100 shadow-sm outline-none transition-colors duration-150 placeholder:text-zinc-500 focus:border-blue-500 focus:bg-zinc-950 focus:ring-2 focus:ring-blue-500/40"
|
||||
data-dusk="ticket-subject"
|
||||
dusk="ticket-subject"
|
||||
autocomplete="off"
|
||||
/>
|
||||
data-dusk="ticket-subject" dusk="ticket-subject" autocomplete="off" />
|
||||
|
||||
@error('subject')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@if($companies->isNotEmpty())
|
||||
@if ($companies->isNotEmpty())
|
||||
<div class="xl:col-span-2">
|
||||
<label class="block text-sm font-semibold text-zinc-200" for="company_id">
|
||||
Company <span class="helpdesk-required-star">*</span>
|
||||
</label>
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<select
|
||||
id="company_id"
|
||||
dusk="ticket-company"
|
||||
data-helpdesk-select2
|
||||
data-placeholder="Select option"
|
||||
data-wire-model="company_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
>
|
||||
<select id="company_id" dusk="ticket-company" data-helpdesk-select2
|
||||
data-placeholder="Select option" data-wire-model="company_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
<option value="">Select company</option>
|
||||
@foreach ($companies as $company)
|
||||
<option value="{{ $company->id }}" @selected((string) $company_id === (string) $company->id)>{{ $company->name }}</option>
|
||||
<option value="{{ $company->id }}" @selected((string) $company_id === (string) $company->id)>
|
||||
{{ $company->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('company_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
@endif
|
||||
@@ -75,24 +62,20 @@
|
||||
</label>
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<select
|
||||
id="board_id"
|
||||
data-helpdesk-select2
|
||||
data-placeholder="Select option"
|
||||
<select id="board_id" data-helpdesk-select2 data-placeholder="Select option"
|
||||
data-wire-model="board_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
data-dusk="ticket-board"
|
||||
dusk="ticket-board"
|
||||
>
|
||||
data-dusk="ticket-board" dusk="ticket-board">
|
||||
<option value="">No board</option>
|
||||
@foreach ($boards as $board)
|
||||
<option value="{{ $board->id }}" @selected((int) $board_id === (int) $board->id)>{{ $board->name }}{{ $board->owner ? ' · '.$board->owner->name : '' }}</option>
|
||||
<option value="{{ $board->id }}" @selected((int) $board_id === (int) $board->id)>
|
||||
{{ $board->name }}{{ $board->owner ? ' · ' . $board->owner->name : '' }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('board_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -102,24 +85,20 @@
|
||||
</label>
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<select
|
||||
id="priority_id"
|
||||
data-helpdesk-select2
|
||||
data-placeholder="Select option"
|
||||
<select id="priority_id" data-helpdesk-select2 data-placeholder="Select option"
|
||||
data-wire-model="priority_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
data-dusk="ticket-priority"
|
||||
dusk="ticket-priority"
|
||||
>
|
||||
data-dusk="ticket-priority" dusk="ticket-priority">
|
||||
<option value="">Select priority</option>
|
||||
@foreach ($priorities as $priority)
|
||||
<option value="{{ $priority->id }}" @selected((string) $priority_id === (string) $priority->id)>{{ $priority->name }}</option>
|
||||
<option value="{{ $priority->id }}" @selected((string) $priority_id === (string) $priority->id)>{{ $priority->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('priority_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -129,22 +108,19 @@
|
||||
</label>
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<select
|
||||
id="category_id"
|
||||
data-helpdesk-select2
|
||||
data-placeholder="Select option"
|
||||
<select id="category_id" data-helpdesk-select2 data-placeholder="Select option"
|
||||
data-wire-model="category_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
>
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
<option value="">Select category</option>
|
||||
@foreach ($categories as $category)
|
||||
<option value="{{ $category->id }}" @selected((string) $category_id === (string) $category->id)>{{ $category->name }}</option>
|
||||
<option value="{{ $category->id }}" @selected((string) $category_id === (string) $category->id)>{{ $category->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('category_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -154,22 +130,19 @@
|
||||
</label>
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<select
|
||||
id="project_id"
|
||||
data-helpdesk-select2
|
||||
data-placeholder="Select option"
|
||||
<select id="project_id" data-helpdesk-select2 data-placeholder="Select option"
|
||||
data-wire-model="project_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
>
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
<option value="">Select project</option>
|
||||
@foreach ($projects as $project)
|
||||
<option value="{{ $project->id }}" @selected((string) $project_id === (string) $project->id)>{{ $project->title ?? $project->name }}</option>
|
||||
<option value="{{ $project->id }}" @selected((string) $project_id === (string) $project->id)>
|
||||
{{ $project->title ?? $project->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('project_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -179,22 +152,19 @@
|
||||
</label>
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<select
|
||||
id="team_id"
|
||||
data-helpdesk-select2
|
||||
data-placeholder="Select option"
|
||||
<select id="team_id" data-helpdesk-select2 data-placeholder="Select option"
|
||||
data-wire-model="team_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
>
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
<option value="">Select team</option>
|
||||
@foreach ($teams as $team)
|
||||
<option value="{{ $team->id }}" @selected((string) $team_id === (string) $team->id)>{{ $team->name }}</option>
|
||||
<option value="{{ $team->id }}" @selected((string) $team_id === (string) $team->id)>{{ $team->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('team_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -212,50 +182,40 @@
|
||||
|
||||
<div class="mt-4 grid gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
@forelse ($tags as $tag)
|
||||
<label
|
||||
for="tag_{{ $tag->id }}"
|
||||
class="flex cursor-pointer items-center gap-3 rounded-xl border border-zinc-700 bg-zinc-900 px-4 py-3 text-sm font-medium text-zinc-200 transition hover:border-blue-500 hover:bg-zinc-800"
|
||||
>
|
||||
<input
|
||||
id="tag_{{ $tag->id }}"
|
||||
type="checkbox"
|
||||
wire:model.defer="tag_ids"
|
||||
<label for="tag_{{ $tag->id }}"
|
||||
class="flex cursor-pointer items-center gap-3 rounded-xl border border-zinc-700 bg-zinc-900 px-4 py-3 text-sm font-medium text-zinc-200 transition hover:border-blue-500 hover:bg-zinc-800">
|
||||
<input id="tag_{{ $tag->id }}" type="checkbox" wire:model.defer="tag_ids"
|
||||
value="{{ $tag->id }}"
|
||||
class="h-4 w-4 rounded border-zinc-600 bg-zinc-950 text-blue-600 focus:ring-blue-500"
|
||||
>
|
||||
class="h-4 w-4 rounded border-zinc-600 bg-zinc-950 text-blue-600 focus:ring-blue-500">
|
||||
|
||||
<span class="truncate">
|
||||
{{ $tag->name }}
|
||||
</span>
|
||||
</label>
|
||||
@empty
|
||||
<div class="rounded-xl border border-dashed border-zinc-700 bg-zinc-900 px-4 py-3 text-sm text-zinc-400">
|
||||
<div
|
||||
class="rounded-xl border border-dashed border-zinc-700 bg-zinc-900 px-4 py-3 text-sm text-zinc-400">
|
||||
No tags are available.
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
@error('tag_ids')
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
@error('tag_ids.*')
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="xl:col-span-2">
|
||||
<x-helpdesk::date-picker
|
||||
id="due_at"
|
||||
model="due_at"
|
||||
label="Due date"
|
||||
placeholder="Select due date"
|
||||
value="{{ $due_at }}"
|
||||
/>
|
||||
<x-helpdesk::date-picker id="due_at" model="due_at" label="Due date"
|
||||
placeholder="Select due date" value="{{ $due_at }}" />
|
||||
|
||||
@error('due_at')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -264,56 +224,60 @@
|
||||
Details <span class="helpdesk-required-star">*</span>
|
||||
</label>
|
||||
|
||||
<div class="mt-2 overflow-hidden rounded-xl border border-zinc-700 bg-zinc-950 shadow-sm focus-within:border-blue-500 focus-within:ring-2 focus-within:ring-blue-500/40" wire:ignore>
|
||||
<div class="mt-2 overflow-hidden rounded-xl border border-zinc-700 bg-zinc-950 shadow-sm focus-within:border-blue-500 focus-within:ring-2 focus-within:ring-blue-500/40"
|
||||
wire:ignore>
|
||||
<div class="flex flex-wrap items-center gap-1 border-b border-zinc-700 bg-zinc-900 px-3 py-2">
|
||||
<button type="button" class="helpdesk-editor-button" data-helpdesk-editor-command="bold" aria-label="Bold">
|
||||
<button type="button" class="helpdesk-editor-button" data-helpdesk-editor-command="bold"
|
||||
aria-label="Bold">
|
||||
<i class="fa-solid fa-bold" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<button type="button" class="helpdesk-editor-button" data-helpdesk-editor-command="italic" aria-label="Italic">
|
||||
<button type="button" class="helpdesk-editor-button"
|
||||
data-helpdesk-editor-command="italic" aria-label="Italic">
|
||||
<i class="fa-solid fa-italic" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<button type="button" class="helpdesk-editor-button" data-helpdesk-editor-command="underline" aria-label="Underline">
|
||||
<button type="button" class="helpdesk-editor-button"
|
||||
data-helpdesk-editor-command="underline" aria-label="Underline">
|
||||
<i class="fa-solid fa-underline" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<span class="mx-1 h-6 w-px bg-zinc-700" aria-hidden="true"></span>
|
||||
|
||||
<button type="button" class="helpdesk-editor-button" data-helpdesk-editor-command="insertUnorderedList" aria-label="Bullet list">
|
||||
<button type="button" class="helpdesk-editor-button"
|
||||
data-helpdesk-editor-command="insertUnorderedList" aria-label="Bullet list">
|
||||
<i class="fa-solid fa-list-ul" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<button type="button" class="helpdesk-editor-button" data-helpdesk-editor-command="insertOrderedList" aria-label="Numbered list">
|
||||
<button type="button" class="helpdesk-editor-button"
|
||||
data-helpdesk-editor-command="insertOrderedList" aria-label="Numbered list">
|
||||
<i class="fa-solid fa-list-ol" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<span class="mx-1 h-6 w-px bg-zinc-700" aria-hidden="true"></span>
|
||||
|
||||
<button type="button" class="helpdesk-editor-button" data-helpdesk-editor-format="pre" aria-label="Code block">
|
||||
<button type="button" class="helpdesk-editor-button" data-helpdesk-editor-format="pre"
|
||||
aria-label="Code block">
|
||||
<i class="fa-solid fa-code" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<button type="button" class="helpdesk-editor-button" data-helpdesk-editor-link aria-label="Insert link">
|
||||
<button type="button" class="helpdesk-editor-button" data-helpdesk-editor-link
|
||||
aria-label="Insert link">
|
||||
<i class="fa-solid fa-link" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="body-editor"
|
||||
<div id="body-editor"
|
||||
class="min-h-[18rem] w-full px-4 py-3 text-sm leading-6 text-zinc-100 outline-none empty:before:text-zinc-500 empty:before:content-[attr(data-placeholder)]"
|
||||
contenteditable="true"
|
||||
data-helpdesk-details-editor
|
||||
contenteditable="true" data-helpdesk-details-editor
|
||||
data-placeholder="Describe the issue, expected outcome, steps to reproduce and any useful context..."
|
||||
data-dusk="ticket-body-editor"
|
||||
dusk="ticket-body-editor"
|
||||
>{!! $body ?? '' !!}</div>
|
||||
data-dusk="ticket-body-editor" dusk="ticket-body-editor">{!! $body ?? '' !!}</div>
|
||||
</div>
|
||||
|
||||
<textarea id="body" wire:model.defer="body" class="sr-only" tabindex="-1" aria-hidden="true"></textarea>
|
||||
|
||||
@error('body')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -330,63 +294,46 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<label
|
||||
for="attachments"
|
||||
class="inline-flex cursor-pointer items-center justify-center gap-2 rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-zinc-950"
|
||||
>
|
||||
<label for="attachments"
|
||||
class="inline-flex cursor-pointer items-center justify-center gap-2 rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-zinc-950">
|
||||
<i class="fa-solid fa-paperclip" aria-hidden="true"></i>
|
||||
<span>Add attachments</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input
|
||||
id="attachments"
|
||||
type="file"
|
||||
wire:model="attachments"
|
||||
multiple
|
||||
class="sr-only"
|
||||
>
|
||||
<input id="attachments" type="file" wire:model="attachments" multiple class="sr-only">
|
||||
|
||||
<div
|
||||
wire:loading.flex
|
||||
wire:target="attachments"
|
||||
class="mt-4 items-center gap-2 rounded-xl border border-blue-500/30 bg-blue-500/10 px-4 py-3 text-sm font-medium text-blue-200"
|
||||
>
|
||||
<div wire:loading.flex wire:target="attachments"
|
||||
class="mt-4 items-center gap-2 rounded-xl border border-blue-500/30 bg-blue-500/10 px-4 py-3 text-sm font-medium text-blue-200">
|
||||
<i class="fa-solid fa-circle-notch fa-spin" aria-hidden="true"></i>
|
||||
Uploading attachments...
|
||||
</div>
|
||||
|
||||
@error('attachments')
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
@error('attachments.*')
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end gap-3 border-t border-zinc-700 bg-zinc-900 px-6 py-4">
|
||||
<button
|
||||
type="button"
|
||||
wire:click="clearForm"
|
||||
class="rounded-xl bg-red-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-700"
|
||||
>
|
||||
<button type="button" wire:click="clearForm"
|
||||
class="rounded-xl bg-red-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-700">
|
||||
Clear
|
||||
</button>
|
||||
|
||||
<a href="{{ route('helpdesk.tickets.index') }}" wire:navigate class="rounded-xl bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">
|
||||
<a href="{{ route('helpdesk.tickets.index') }}" wire:navigate
|
||||
class="rounded-xl bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">
|
||||
Cancel
|
||||
</a>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
<button type="submit"
|
||||
class="inline-flex items-center gap-2 rounded-xl bg-green-600 px-5 py-2 text-sm font-semibold text-white shadow-sm hover:bg-green-700 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
data-dusk="ticket-create-submit"
|
||||
dusk="ticket-create-submit"
|
||||
wire:loading.attr="disabled"
|
||||
>
|
||||
data-dusk="ticket-create-submit" dusk="ticket-create-submit" wire:loading.attr="disabled">
|
||||
<i class="fa-regular fa-floppy-disk" aria-hidden="true"></i>
|
||||
<span wire:loading.remove>Create Ticket</span>
|
||||
<span wire:loading>Creating...</span>
|
||||
@@ -414,7 +361,7 @@
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const form = document.querySelector('.helpdesk-ticket-form');
|
||||
const editor = document.querySelector('[data-helpdesk-details-editor]');
|
||||
const bodyField = document.getElementById('body');
|
||||
@@ -423,30 +370,34 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const syncBody = function () {
|
||||
const syncBody = function() {
|
||||
bodyField.value = editor.innerHTML.trim();
|
||||
bodyField.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
bodyField.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
bodyField.dispatchEvent(new Event('input', {
|
||||
bubbles: true
|
||||
}));
|
||||
bodyField.dispatchEvent(new Event('change', {
|
||||
bubbles: true
|
||||
}));
|
||||
};
|
||||
|
||||
document.querySelectorAll('[data-helpdesk-editor-command]').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
document.querySelectorAll('[data-helpdesk-editor-command]').forEach(function(button) {
|
||||
button.addEventListener('click', function() {
|
||||
editor.focus();
|
||||
document.execCommand(button.dataset.helpdeskEditorCommand, false, null);
|
||||
syncBody();
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-helpdesk-editor-format]').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
document.querySelectorAll('[data-helpdesk-editor-format]').forEach(function(button) {
|
||||
button.addEventListener('click', function() {
|
||||
editor.focus();
|
||||
document.execCommand('formatBlock', false, button.dataset.helpdeskEditorFormat);
|
||||
syncBody();
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-helpdesk-editor-link]').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
document.querySelectorAll('[data-helpdesk-editor-link]').forEach(function(button) {
|
||||
button.addEventListener('click', function() {
|
||||
const url = window.prompt('Enter link URL');
|
||||
|
||||
if (!url) {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<div class="space-y-6">
|
||||
@vite([
|
||||
'resources/css/app.css',
|
||||
'resources/js/app.js',
|
||||
])
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
@include('helpdesk::partials.page-header', [
|
||||
'eyebrow' => 'Helpdesk',
|
||||
'title' => 'Edit Ticket',
|
||||
'subtitle' => 'Update ownership, assignment, workflow status, product/service, project, team, category, priority and ticket details.',
|
||||
'subtitle' =>
|
||||
'Update ownership, assignment, workflow status, product/service, project, team, category, priority and ticket details.',
|
||||
])
|
||||
|
||||
<form wire:submit.prevent="save" class="space-y-6">
|
||||
@@ -19,7 +17,8 @@
|
||||
{{ $ticket->reference }} · Ticket controls
|
||||
</h2>
|
||||
|
||||
<span class="inline-flex items-center gap-2 rounded-xl border border-zinc-700 bg-zinc-900 px-3 py-1.5 text-sm font-medium text-zinc-200">
|
||||
<span
|
||||
class="inline-flex items-center gap-2 rounded-xl border border-zinc-700 bg-zinc-900 px-3 py-1.5 text-sm font-medium text-zinc-200">
|
||||
<i class="fa-regular fa-user text-zinc-400" aria-hidden="true"></i>
|
||||
Owner:
|
||||
<span class="font-semibold text-white">{{ $ticket->owner?->name ?? 'Unknown' }}</span>
|
||||
@@ -32,7 +31,8 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a href="{{ route('helpdesk.tickets.show', $ticket) }}" wire:navigate class="inline-flex items-center justify-center rounded-xl bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">
|
||||
<a href="{{ route('helpdesk.tickets.show', $ticket) }}" wire:navigate
|
||||
class="inline-flex items-center justify-center rounded-xl bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">
|
||||
Back to ticket
|
||||
</a>
|
||||
</div>
|
||||
@@ -44,44 +44,35 @@
|
||||
Subject <span class="helpdesk-required-star">*</span>
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="subject"
|
||||
type="text"
|
||||
maxlength="180"
|
||||
wire:model.defer="subject"
|
||||
<input id="subject" type="text" maxlength="180" wire:model.defer="subject"
|
||||
class="mt-2 block h-14 w-full max-w-4xl rounded-xl border border-zinc-700 bg-zinc-950 px-4 text-base font-medium text-zinc-100 shadow-sm outline-none transition-colors duration-150 placeholder:text-zinc-500 focus:border-blue-500 focus:bg-zinc-950 focus:ring-2 focus:ring-blue-500/40"
|
||||
data-dusk="ticket-edit-subject"
|
||||
autocomplete="off"
|
||||
/>
|
||||
data-dusk="ticket-edit-subject" autocomplete="off" />
|
||||
|
||||
@error('subject')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@if($companies->isNotEmpty())
|
||||
@if ($companies->isNotEmpty())
|
||||
<div class="xl:col-span-2">
|
||||
<label class="block text-sm font-semibold text-zinc-200" for="company_id">
|
||||
Company <span class="helpdesk-required-star">*</span>
|
||||
</label>
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<select
|
||||
id="company_id"
|
||||
data-helpdesk-select2
|
||||
data-placeholder="Select option"
|
||||
<select id="company_id" data-helpdesk-select2 data-placeholder="Select option"
|
||||
data-wire-model="company_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
>
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
<option value="">Select company</option>
|
||||
@foreach ($companies as $company)
|
||||
<option value="{{ $company->id }}" @selected((string) $company_id === (string) $company->id)>{{ $company->name }}</option>
|
||||
<option value="{{ $company->id }}" @selected((string) $company_id === (string) $company->id)>
|
||||
{{ $company->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('company_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
@endif
|
||||
@@ -90,23 +81,20 @@
|
||||
<label class="block text-sm font-semibold text-zinc-200" for="board_id">Board</label>
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<select
|
||||
id="board_id"
|
||||
data-helpdesk-select2
|
||||
data-placeholder="Select option"
|
||||
<select id="board_id" data-helpdesk-select2 data-placeholder="Select option"
|
||||
data-wire-model="board_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
data-dusk="ticket-edit-board"
|
||||
>
|
||||
data-dusk="ticket-edit-board">
|
||||
<option value="">No board</option>
|
||||
@foreach ($boards as $board)
|
||||
<option value="{{ $board->id }}" @selected((string) $board_id === (string) $board->id)>{{ $board->name }}{{ $board->owner ? ' · '.$board->owner->name : '' }}</option>
|
||||
<option value="{{ $board->id }}" @selected((string) $board_id === (string) $board->id)>
|
||||
{{ $board->name }}{{ $board->owner ? ' · ' . $board->owner->name : '' }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('board_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -116,23 +104,20 @@
|
||||
</label>
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<select
|
||||
id="status_id"
|
||||
data-helpdesk-select2
|
||||
data-placeholder="Select option"
|
||||
<select id="status_id" data-helpdesk-select2 data-placeholder="Select option"
|
||||
data-wire-model="status_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
data-dusk="ticket-edit-status"
|
||||
>
|
||||
data-dusk="ticket-edit-status">
|
||||
<option value="">No status</option>
|
||||
@foreach ($statuses as $status)
|
||||
<option value="{{ $status->id }}" @selected((string) $status_id === (string) $status->id)>{{ $status->name }}</option>
|
||||
<option value="{{ $status->id }}" @selected((string) $status_id === (string) $status->id)>{{ $status->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('status_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -142,23 +127,20 @@
|
||||
</label>
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<select
|
||||
id="priority_id"
|
||||
data-helpdesk-select2
|
||||
data-placeholder="Select option"
|
||||
<select id="priority_id" data-helpdesk-select2 data-placeholder="Select option"
|
||||
data-wire-model="priority_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
data-dusk="ticket-edit-priority"
|
||||
>
|
||||
data-dusk="ticket-edit-priority">
|
||||
<option value="">Select priority</option>
|
||||
@foreach ($priorities as $priority)
|
||||
<option value="{{ $priority->id }}" @selected((string) $priority_id === (string) $priority->id)>{{ $priority->name }}</option>
|
||||
<option value="{{ $priority->id }}" @selected((string) $priority_id === (string) $priority->id)>{{ $priority->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('priority_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -166,15 +148,13 @@
|
||||
<label class="block text-sm font-semibold text-zinc-200" for="agent_id">Assigned agent</label>
|
||||
|
||||
<div class="mt-2">
|
||||
<select
|
||||
id="agent_id"
|
||||
wire:model.live="agent_id"
|
||||
<select id="agent_id" wire:model.live="agent_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
data-dusk="ticket-edit-agent"
|
||||
>
|
||||
data-dusk="ticket-edit-agent">
|
||||
<option value="">Unassigned</option>
|
||||
@forelse ($agents as $agent)
|
||||
<option value="{{ $agent->id }}">{{ $agent->name }} · {{ $agent->email }}</option>
|
||||
<option value="{{ $agent->id }}">{{ $agent->name }} · {{ $agent->email }}
|
||||
</option>
|
||||
@empty
|
||||
<option value="" disabled>No users available for assignment</option>
|
||||
@endforelse
|
||||
@@ -182,7 +162,7 @@
|
||||
</div>
|
||||
|
||||
@error('agent_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -192,22 +172,19 @@
|
||||
</label>
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<select
|
||||
id="category_id"
|
||||
data-helpdesk-select2
|
||||
data-placeholder="Select option"
|
||||
<select id="category_id" data-helpdesk-select2 data-placeholder="Select option"
|
||||
data-wire-model="category_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
>
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
<option value="">No category</option>
|
||||
@foreach ($categories as $category)
|
||||
<option value="{{ $category->id }}" @selected((string) $category_id === (string) $category->id)>{{ $category->name }}</option>
|
||||
<option value="{{ $category->id }}" @selected((string) $category_id === (string) $category->id)>
|
||||
{{ $category->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('category_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -217,22 +194,19 @@
|
||||
</label>
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<select
|
||||
id="project_id"
|
||||
data-helpdesk-select2
|
||||
data-placeholder="Select option"
|
||||
<select id="project_id" data-helpdesk-select2 data-placeholder="Select option"
|
||||
data-wire-model="project_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
>
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
<option value="">No project</option>
|
||||
@foreach ($projects as $project)
|
||||
<option value="{{ $project->id }}" @selected((string) $project_id === (string) $project->id)>{{ $project->title ?? $project->name }}</option>
|
||||
<option value="{{ $project->id }}" @selected((string) $project_id === (string) $project->id)>
|
||||
{{ $project->title ?? $project->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('project_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -242,22 +216,19 @@
|
||||
</label>
|
||||
|
||||
<div class="mt-2" wire:ignore>
|
||||
<select
|
||||
id="team_id"
|
||||
data-helpdesk-select2
|
||||
data-placeholder="Select option"
|
||||
<select id="team_id" data-helpdesk-select2 data-placeholder="Select option"
|
||||
data-wire-model="team_id"
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
||||
>
|
||||
class="block w-full rounded-xl border-zinc-700 bg-zinc-950 text-zinc-100 shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
<option value="">No team</option>
|
||||
@foreach ($teams as $team)
|
||||
<option value="{{ $team->id }}" @selected((string) $team_id === (string) $team->id)>{{ $team->name }}</option>
|
||||
<option value="{{ $team->id }}" @selected((string) $team_id === (string) $team->id)>{{ $team->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('team_id')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@@ -275,68 +246,63 @@
|
||||
|
||||
<div class="mt-4 grid gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
@forelse ($tags as $tag)
|
||||
<label
|
||||
for="tag_{{ $tag->id }}"
|
||||
class="flex cursor-pointer items-center gap-3 rounded-xl border border-zinc-700 bg-zinc-900 px-4 py-3 text-sm font-medium text-zinc-200 transition hover:border-blue-500 hover:bg-zinc-800"
|
||||
>
|
||||
<input
|
||||
id="tag_{{ $tag->id }}"
|
||||
type="checkbox"
|
||||
wire:model.defer="tag_ids"
|
||||
<label for="tag_{{ $tag->id }}"
|
||||
class="flex cursor-pointer items-center gap-3 rounded-xl border border-zinc-700 bg-zinc-900 px-4 py-3 text-sm font-medium text-zinc-200 transition hover:border-blue-500 hover:bg-zinc-800">
|
||||
<input id="tag_{{ $tag->id }}" type="checkbox" wire:model.defer="tag_ids"
|
||||
value="{{ $tag->id }}"
|
||||
class="h-4 w-4 rounded border-zinc-600 bg-zinc-950 text-blue-600 focus:ring-blue-500"
|
||||
>
|
||||
class="h-4 w-4 rounded border-zinc-600 bg-zinc-950 text-blue-600 focus:ring-blue-500">
|
||||
|
||||
<span class="truncate">
|
||||
{{ $tag->name }}
|
||||
</span>
|
||||
</label>
|
||||
@empty
|
||||
<div class="rounded-xl border border-dashed border-zinc-700 bg-zinc-900 px-4 py-3 text-sm text-zinc-400">
|
||||
<div
|
||||
class="rounded-xl border border-dashed border-zinc-700 bg-zinc-900 px-4 py-3 text-sm text-zinc-400">
|
||||
No tags are available.
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
@error('tag_ids')
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
@error('tag_ids.*')
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="xl:col-span-2">
|
||||
<x-helpdesk::date-picker
|
||||
id="due_at"
|
||||
model="due_at"
|
||||
label="Due date"
|
||||
placeholder="Select due date"
|
||||
value="{{ $due_at }}"
|
||||
/>
|
||||
<x-helpdesk::date-picker id="due_at" model="due_at" label="Due date"
|
||||
placeholder="Select due date" value="{{ $due_at }}" />
|
||||
|
||||
@error('due_at')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-3 xl:col-span-6">
|
||||
<x-helpdesk::html-editor dusk="ticket-edit-body-editor" wire:model="body" label="Details *" rows="10" placeholder="Update the ticket details..." />
|
||||
<x-helpdesk::html-editor dusk="ticket-edit-body-editor" wire:model="body" label="Details *"
|
||||
rows="10" placeholder="Update the ticket details..." />
|
||||
|
||||
@error('body')
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-3 xl:col-span-6">
|
||||
<div class="overflow-hidden rounded-2xl border border-blue-500/30 bg-zinc-950/70 shadow-sm shadow-blue-950/30">
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl border border-blue-500/30 bg-zinc-950/70 shadow-sm shadow-blue-950/30">
|
||||
<div class="bg-gradient-to-r from-blue-700 via-sky-600 to-emerald-600 px-5 py-4 text-white">
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<label class="flex items-center gap-2 text-base font-extrabold text-white drop-shadow-sm" for="attachments">
|
||||
<span class="inline-flex h-9 w-9 items-center justify-center rounded-xl bg-white/15 ring-1 ring-white/25">
|
||||
<label
|
||||
class="flex items-center gap-2 text-base font-extrabold text-white drop-shadow-sm"
|
||||
for="attachments">
|
||||
<span
|
||||
class="inline-flex h-9 w-9 items-center justify-center rounded-xl bg-white/15 ring-1 ring-white/25">
|
||||
<i class="fa-solid fa-paperclip" aria-hidden="true"></i>
|
||||
</span>
|
||||
Attachments
|
||||
@@ -347,10 +313,8 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<label
|
||||
for="attachments"
|
||||
class="inline-flex cursor-pointer items-center justify-center gap-2 rounded-xl bg-white px-4 py-2.5 text-sm font-bold text-blue-700 shadow-sm transition hover:bg-blue-50 focus:outline-none focus:ring-2 focus:ring-white/80 focus:ring-offset-2 focus:ring-offset-blue-700"
|
||||
>
|
||||
<label for="attachments"
|
||||
class="inline-flex cursor-pointer items-center justify-center gap-2 rounded-xl bg-white px-4 py-2.5 text-sm font-bold text-blue-700 shadow-sm transition hover:bg-blue-50 focus:outline-none focus:ring-2 focus:ring-white/80 focus:ring-offset-2 focus:ring-offset-blue-700">
|
||||
<i class="fa-solid fa-plus" aria-hidden="true"></i>
|
||||
<span>Add attachments</span>
|
||||
</label>
|
||||
@@ -359,100 +323,90 @@
|
||||
|
||||
<div class="p-4">
|
||||
|
||||
<input
|
||||
id="attachments"
|
||||
type="file"
|
||||
wire:model="attachments"
|
||||
multiple
|
||||
class="sr-only"
|
||||
>
|
||||
<input id="attachments" type="file" wire:model="attachments" multiple
|
||||
class="sr-only">
|
||||
|
||||
<div
|
||||
wire:loading.flex
|
||||
wire:target="attachments"
|
||||
class="mt-4 items-center gap-2 rounded-xl border border-blue-500/30 bg-blue-500/10 px-4 py-3 text-sm font-medium text-blue-200"
|
||||
>
|
||||
<i class="fa-solid fa-circle-notch fa-spin" aria-hidden="true"></i>
|
||||
Uploading attachments...
|
||||
</div>
|
||||
|
||||
@if ($ticket->attachments->isNotEmpty())
|
||||
<div class="mt-4 space-y-3">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3 rounded-xl border border-emerald-500/30 bg-emerald-500/10 px-4 py-3">
|
||||
<h3 class="inline-flex items-center gap-2 text-sm font-extrabold text-emerald-200">
|
||||
<span class="inline-flex h-7 w-7 items-center justify-center rounded-lg bg-emerald-500 text-white shadow-sm">
|
||||
<i class="fa-solid fa-folder-open" aria-hidden="true"></i>
|
||||
</span>
|
||||
Existing attachments
|
||||
</h3>
|
||||
|
||||
<span class="inline-flex items-center gap-2 rounded-full bg-emerald-100 px-3 py-1 text-xs font-extrabold text-emerald-800 shadow-sm ring-1 ring-emerald-300/70">
|
||||
<span class="inline-flex h-2 w-2 rounded-full bg-emerald-500"></span>
|
||||
{{ $ticket->attachments->count() }} file(s)
|
||||
</span>
|
||||
<div wire:loading.flex wire:target="attachments"
|
||||
class="mt-4 items-center gap-2 rounded-xl border border-blue-500/30 bg-blue-500/10 px-4 py-3 text-sm font-medium text-blue-200">
|
||||
<i class="fa-solid fa-circle-notch fa-spin" aria-hidden="true"></i>
|
||||
Uploading attachments...
|
||||
</div>
|
||||
|
||||
@foreach ($ticket->attachments as $attachment)
|
||||
<div class="flex flex-col gap-3 rounded-xl border border-zinc-700 bg-zinc-900 p-3 text-sm sm:flex-row sm:items-center sm:justify-between">
|
||||
<a
|
||||
href="{{ route('helpdesk.attachments.download', $attachment->id) }}"
|
||||
target="_blank"
|
||||
class="min-w-0 font-medium text-blue-300 hover:text-blue-200"
|
||||
>
|
||||
<i class="fa-regular fa-file mr-2" aria-hidden="true"></i>
|
||||
{{ $attachment->original_name ?? $attachment->original_filename ?? $attachment->filename ?? $attachment->file_name ?? basename($attachment->path ?? $attachment->file_path ?? '') }}
|
||||
</a>
|
||||
@if ($ticket->attachments->isNotEmpty())
|
||||
<div class="mt-4 space-y-3">
|
||||
<div
|
||||
class="flex flex-wrap items-center justify-between gap-3 rounded-xl border border-emerald-500/30 bg-emerald-500/10 px-4 py-3">
|
||||
<h3
|
||||
class="inline-flex items-center gap-2 text-sm font-extrabold text-emerald-200">
|
||||
<span
|
||||
class="inline-flex h-7 w-7 items-center justify-center rounded-lg bg-emerald-500 text-white shadow-sm">
|
||||
<i class="fa-solid fa-folder-open" aria-hidden="true"></i>
|
||||
</span>
|
||||
Existing attachments
|
||||
</h3>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
wire:click="removeAttachment({{ $attachment->id }})"
|
||||
wire:confirm="Remove this attachment?"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-red-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-red-700"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
<span
|
||||
class="inline-flex items-center gap-2 rounded-full bg-emerald-100 px-3 py-1 text-xs font-extrabold text-emerald-800 shadow-sm ring-1 ring-emerald-300/70">
|
||||
<span class="inline-flex h-2 w-2 rounded-full bg-emerald-500"></span>
|
||||
{{ $ticket->attachments->count() }} file(s)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@foreach ($ticket->attachments as $attachment)
|
||||
<div
|
||||
class="flex flex-col gap-3 rounded-xl border border-zinc-700 bg-zinc-900 p-3 text-sm sm:flex-row sm:items-center sm:justify-between">
|
||||
<a href="{{ route('helpdesk.attachments.download', $attachment->id) }}"
|
||||
target="_blank"
|
||||
class="min-w-0 font-medium text-blue-300 hover:text-blue-200">
|
||||
<i class="fa-regular fa-file mr-2" aria-hidden="true"></i>
|
||||
{{ $attachment->original_name ?? ($attachment->original_filename ?? ($attachment->filename ?? ($attachment->file_name ?? basename($attachment->path ?? ($attachment->file_path ?? ''))))) }}
|
||||
</a>
|
||||
|
||||
<button type="button"
|
||||
wire:click="removeAttachment({{ $attachment->id }})"
|
||||
wire:confirm="Remove this attachment?"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-red-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-red-700">
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if ($attachments !== [])
|
||||
<div class="mt-4 rounded-xl border border-blue-500/30 bg-blue-500/10 px-4 py-3 text-sm text-blue-100">
|
||||
{{ count($attachments) }} new attachment(s) selected. Save the ticket to attach them.
|
||||
</div>
|
||||
@endif
|
||||
@if ($attachments !== [])
|
||||
<div
|
||||
class="mt-4 rounded-xl border border-blue-500/30 bg-blue-500/10 px-4 py-3 text-sm text-blue-100">
|
||||
{{ count($attachments) }} new attachment(s) selected. Save the ticket to attach
|
||||
them.
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@error('attachments')
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
@error('attachments')
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
@error('attachments.*')
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
@error('attachments.*')
|
||||
<p class="mt-3 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end gap-3 border-t border-zinc-700 bg-zinc-900 px-6 py-4">
|
||||
<button
|
||||
type="button"
|
||||
wire:click="clearForm"
|
||||
class="rounded-xl bg-red-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-700"
|
||||
>
|
||||
<button type="button" wire:click="clearForm"
|
||||
class="rounded-xl bg-red-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-700">
|
||||
Clear
|
||||
</button>
|
||||
|
||||
<a href="{{ route('helpdesk.tickets.show', $ticket) }}" wire:navigate class="rounded-xl bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">
|
||||
<a href="{{ route('helpdesk.tickets.show', $ticket) }}" wire:navigate
|
||||
class="rounded-xl bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">
|
||||
Cancel
|
||||
</a>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
<button type="submit"
|
||||
class="inline-flex items-center gap-2 rounded-xl bg-green-600 px-5 py-2 text-sm font-semibold text-white shadow-sm hover:bg-green-700 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
data-dusk="ticket-edit-submit"
|
||||
wire:loading.attr="disabled"
|
||||
>
|
||||
data-dusk="ticket-edit-submit" wire:loading.attr="disabled">
|
||||
<i class="fa-regular fa-floppy-disk" aria-hidden="true"></i>
|
||||
<span wire:loading.remove>Save Ticket</span>
|
||||
<span wire:loading>Saving...</span>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<div class="hd-helpdesk-surface space-y-6" dusk="helpdesk-ticket-index">
|
||||
|
||||
<div class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="bg-gradient-to-br from-slate-950 via-slate-900 to-blue-950 px-6 py-7 text-white">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-blue-200">Ticket management</p>
|
||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight">Active tickets</h1>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">Search, triage and manage support requests across companies, projects, teams, priorities and statuses.</p>
|
||||
<p class="mt-2 max-w-3xl text-sm text-blue-100/90">Search, triage and manage support requests across
|
||||
companies, projects, teams, priorities and statuses.</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a href="{{ route('helpdesk.index') }}" wire:navigate class="hd-btn hd-btn-blue">
|
||||
@@ -24,7 +25,8 @@
|
||||
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="grid gap-4 md:grid-cols-4">
|
||||
<flux:input wire:model.live.debounce.350ms="search" icon="magnifying-glass" placeholder="Search reference, subject or body" />
|
||||
<flux:input wire:model.live.debounce.350ms="search" icon="magnifying-glass"
|
||||
placeholder="Search reference, subject or body" />
|
||||
|
||||
<flux:select wire:model.live="scope">
|
||||
<option value="open">Open tickets</option>
|
||||
@@ -48,14 +50,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div wire:loading.flex class="items-center gap-2 border-b border-zinc-200 bg-blue-50 px-4 py-3 text-sm text-blue-800 dark:border-zinc-700 dark:bg-blue-950 dark:text-blue-200">
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div wire:loading.flex
|
||||
class="items-center gap-2 border-b border-zinc-200 bg-blue-50 px-4 py-3 text-sm text-blue-800 dark:border-zinc-700 dark:bg-blue-950 dark:text-blue-200">
|
||||
<flux:icon.arrow-path class="size-4 animate-spin" /> Refreshing tickets...
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
|
||||
<thead class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<thead
|
||||
class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
|
||||
<tr>
|
||||
<th class="px-4 py-3">Reference</th>
|
||||
<th class="px-4 py-3">Subject</th>
|
||||
@@ -71,43 +76,59 @@
|
||||
@forelse ($tickets as $ticket)
|
||||
<tr class="hover:bg-zinc-50 dark:hover:bg-zinc-800/70">
|
||||
<td class="whitespace-nowrap px-4 py-3 font-mono text-xs">
|
||||
<a href="{{ route('helpdesk.tickets.show', $ticket) }}" wire:navigate class="font-semibold text-blue-700 hover:underline dark:text-blue-300">
|
||||
<a href="{{ route('helpdesk.tickets.show', $ticket) }}" wire:navigate
|
||||
class="font-semibold text-blue-700 hover:underline dark:text-blue-300">
|
||||
{{ $ticket->reference }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<div class="font-medium text-zinc-900 dark:text-zinc-100">{{ $ticket->subject }}</div>
|
||||
<div class="text-xs text-zinc-500">{{ $ticket->category?->name ?? 'No category' }} @if($ticket->project) · {{ $ticket->project->title }} @endif @if($ticket->team) · {{ $ticket->team->name }} @endif</div>
|
||||
<div class="text-xs text-zinc-500">{{ $ticket->category?->name ?? 'No category' }}
|
||||
@if ($ticket->project)
|
||||
· {{ $ticket->project->title }}
|
||||
@endif @if ($ticket->team)
|
||||
· {{ $ticket->team->name }}
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-3"><flux:badge color="{{ $ticket->status->color }}">{{ $ticket->status->name }}</flux:badge></td>
|
||||
<td class="px-4 py-3"><flux:badge color="{{ $ticket->priority->color }}">{{ $ticket->priority->name }}</flux:badge></td>
|
||||
<td class="px-4 py-3 text-zinc-600 dark:text-zinc-300">{{ $ticket->agent?->name ?? 'Unassigned' }}</td>
|
||||
<td class="px-4 py-3">
|
||||
<flux:badge color="{{ $ticket->status->color }}">{{ $ticket->status->name }}
|
||||
</flux:badge>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<flux:badge color="{{ $ticket->priority->color }}">{{ $ticket->priority->name }}
|
||||
</flux:badge>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-zinc-600 dark:text-zinc-300">
|
||||
{{ $ticket->agent?->name ?? 'Unassigned' }}</td>
|
||||
<td class="whitespace-nowrap px-4 py-3 text-center">
|
||||
@if (($ticket->attachments_count ?? 0) > 0)
|
||||
<span class="inline-flex items-center justify-center gap-1 rounded-full border border-blue-200 bg-blue-50 px-2.5 py-1 text-xs font-black text-blue-700 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-200" title="{{ $ticket->attachments_count }} attachment{{ $ticket->attachments_count === 1 ? '' : 's' }}">
|
||||
<span
|
||||
class="inline-flex items-center justify-center gap-1 rounded-full border border-blue-200 bg-blue-50 px-2.5 py-1 text-xs font-black text-blue-700 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-200"
|
||||
title="{{ $ticket->attachments_count }} attachment{{ $ticket->attachments_count === 1 ? '' : 's' }}">
|
||||
<i class="fa-solid fa-paperclip" aria-hidden="true"></i>
|
||||
<span>{{ $ticket->attachments_count }}</span>
|
||||
<span class="sr-only">attachment{{ $ticket->attachments_count === 1 ? '' : 's' }}</span>
|
||||
<span
|
||||
class="sr-only">attachment{{ $ticket->attachments_count === 1 ? '' : 's' }}</span>
|
||||
</span>
|
||||
@else
|
||||
<span class="inline-flex items-center justify-center rounded-full border border-zinc-200 bg-zinc-50 px-2.5 py-1 text-xs font-bold text-zinc-400 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-500" title="No attachments">
|
||||
<span
|
||||
class="inline-flex items-center justify-center rounded-full border border-zinc-200 bg-zinc-50 px-2.5 py-1 text-xs font-bold text-zinc-400 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-500"
|
||||
title="No attachments">
|
||||
<i class="fa-regular fa-circle" aria-hidden="true"></i>
|
||||
<span class="sr-only">No attachments</span>
|
||||
</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="whitespace-nowrap px-4 py-3 text-zinc-500">{{ $ticket->last_activity_at?->diffForHumans() ?? $ticket->created_at->diffForHumans() }}</td>
|
||||
<td class="whitespace-nowrap px-4 py-3 text-zinc-500">
|
||||
{{ $ticket->last_activity_at?->diffForHumans() ?? $ticket->created_at->diffForHumans() }}
|
||||
</td>
|
||||
<td class="whitespace-nowrap px-4 py-3 text-right">
|
||||
@can('delete', $ticket)
|
||||
<button
|
||||
type="button"
|
||||
wire:click="askToDeleteTicket({{ $ticket->id }})"
|
||||
wire:loading.attr="disabled"
|
||||
wire:target="askToDeleteTicket({{ $ticket->id }})"
|
||||
<button type="button" wire:click="askToDeleteTicket({{ $ticket->id }})"
|
||||
wire:loading.attr="disabled" wire:target="askToDeleteTicket({{ $ticket->id }})"
|
||||
class="hd-btn hd-btn-sm hd-btn-red disabled:cursor-not-allowed disabled:opacity-60"
|
||||
title="Delete {{ $ticket->reference }}"
|
||||
dusk="delete-ticket-{{ $ticket->id }}"
|
||||
>
|
||||
title="Delete {{ $ticket->reference }}" dusk="delete-ticket-{{ $ticket->id }}">
|
||||
<i class="fa-solid fa-trash" aria-hidden="true"></i>
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
@@ -116,7 +137,8 @@
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="8" class="px-4 py-12 text-center text-zinc-500">No tickets matched your filters.</td>
|
||||
<td colspan="8" class="px-4 py-12 text-center text-zinc-500">No tickets matched your
|
||||
filters.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
@@ -129,18 +151,16 @@
|
||||
</div>
|
||||
|
||||
@if ($deleteModalOpen)
|
||||
<div
|
||||
class="fixed inset-0 z-[9998] flex items-center justify-center bg-black/75 px-4 py-6 backdrop-blur-sm"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="delete-ticket-modal-title"
|
||||
<div class="fixed inset-0 z-[9998] flex items-center justify-center bg-black/75 px-4 py-6 backdrop-blur-sm"
|
||||
role="dialog" aria-modal="true" aria-labelledby="delete-ticket-modal-title"
|
||||
wire:key="ticket-delete-modal-{{ $pendingDeleteTicketId ?? 'none' }}"
|
||||
dusk="delete-ticket-confirmation-modal"
|
||||
>
|
||||
<div class="w-full max-w-lg overflow-hidden rounded-3xl border border-red-500/30 bg-zinc-950 text-zinc-100 shadow-2xl">
|
||||
dusk="delete-ticket-confirmation-modal">
|
||||
<div
|
||||
class="w-full max-w-lg overflow-hidden rounded-3xl border border-red-500/30 bg-zinc-950 text-zinc-100 shadow-2xl">
|
||||
<div class="border-b border-red-500/20 bg-red-950/40 px-6 py-5">
|
||||
<div class="flex items-start gap-4">
|
||||
<span class="grid h-12 w-12 shrink-0 place-items-center rounded-2xl bg-red-600 text-white shadow-lg shadow-red-950/40">
|
||||
<span
|
||||
class="grid h-12 w-12 shrink-0 place-items-center rounded-2xl bg-red-600 text-white shadow-lg shadow-red-950/40">
|
||||
<i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i>
|
||||
</span>
|
||||
|
||||
@@ -150,7 +170,9 @@
|
||||
</h2>
|
||||
|
||||
<p class="mt-1 text-sm leading-6 text-red-100/90">
|
||||
This will delete <span class="font-mono font-bold">{{ $pendingDeleteTicketReference }}</span>. This action cannot be undone.
|
||||
This will delete <span
|
||||
class="font-mono font-bold">{{ $pendingDeleteTicketReference }}</span>. This
|
||||
action cannot be undone.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -169,25 +191,18 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col-reverse gap-3 border-t border-zinc-800 bg-zinc-900/80 px-6 py-4 sm:flex-row sm:justify-end">
|
||||
<button
|
||||
type="button"
|
||||
wire:click="cancelTicketDelete"
|
||||
class="hd-btn hd-btn-sm hd-btn-blue"
|
||||
dusk="cancel-delete-ticket"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col-reverse gap-3 border-t border-zinc-800 bg-zinc-900/80 px-6 py-4 sm:flex-row sm:justify-end">
|
||||
<button type="button" wire:click="cancelTicketDelete" class="hd-btn hd-btn-sm hd-btn-blue"
|
||||
dusk="cancel-delete-ticket">
|
||||
<i class="fa-solid fa-xmark" aria-hidden="true"></i>
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
wire:click="confirmTicketDelete"
|
||||
wire:loading.attr="disabled"
|
||||
<button type="button" wire:click="confirmTicketDelete" wire:loading.attr="disabled"
|
||||
wire:target="confirmTicketDelete"
|
||||
class="hd-btn hd-btn-sm hd-btn-red disabled:cursor-not-allowed disabled:opacity-60"
|
||||
dusk="confirm-delete-ticket"
|
||||
>
|
||||
dusk="confirm-delete-ticket">
|
||||
<i class="fa-solid fa-trash" aria-hidden="true"></i>
|
||||
<span wire:loading.remove wire:target="confirmTicketDelete">Delete ticket</span>
|
||||
<span wire:loading wire:target="confirmTicketDelete">Deleting...</span>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,14 +4,24 @@
|
||||
@foreach ($comments as $comment)
|
||||
@php
|
||||
$commentUser = $comment->relationLoaded('user') ? $comment->getRelation('user') : null;
|
||||
$commentAttachments = $comment->relationLoaded('attachments') ? $comment->getRelation('attachments') : collect();
|
||||
$commentAttachments = $comment->relationLoaded('attachments')
|
||||
? $comment->getRelation('attachments')
|
||||
: collect();
|
||||
$commentReplies = $comment->relationLoaded('replies') ? $comment->getRelation('replies') : collect();
|
||||
$replyFormId = 'helpdesk-reply-form-'.$comment->id;
|
||||
$currentTicket = $ticket ?? $comment->ticket ?? null;
|
||||
$replyFormId = 'helpdesk-reply-form-' . $comment->id;
|
||||
$currentTicket = $ticket ?? ($comment->ticket ?? null);
|
||||
$replyCount = $commentReplies->count();
|
||||
$isInternal = (bool) $comment->is_internal;
|
||||
$borderClass = $isInternal ? 'border-purple-400/70 dark:border-purple-600/70' : 'border-zinc-200 dark:border-zinc-700';
|
||||
$railClass = $isInternal ? 'bg-purple-500' : ((int) $level % 3 === 0 ? 'bg-red-500' : (((int) $level % 3 === 1) ? 'bg-blue-500' : 'bg-emerald-500'));
|
||||
$borderClass = $isInternal
|
||||
? 'border-purple-400/70 dark:border-purple-600/70'
|
||||
: 'border-zinc-200 dark:border-zinc-700';
|
||||
$railClass = $isInternal
|
||||
? 'bg-purple-500'
|
||||
: ((int) $level % 3 === 0
|
||||
? 'bg-red-500'
|
||||
: ((int) $level % 3 === 1
|
||||
? 'bg-blue-500'
|
||||
: 'bg-emerald-500'));
|
||||
$commentSubject = trim((string) ($comment->subject ?? ''));
|
||||
|
||||
if ($commentSubject === '') {
|
||||
@@ -19,12 +29,16 @@
|
||||
$commentSubject = Str::limit($plainSubject !== '' ? $plainSubject : 'Response', 72);
|
||||
}
|
||||
|
||||
if ($isInternal && ! Str::startsWith(Str::lower($commentSubject), 'internal')) {
|
||||
if ($isInternal && !Str::startsWith(Str::lower($commentSubject), 'internal')) {
|
||||
$commentSubject = 'Internal note';
|
||||
}
|
||||
$renderedBody = html_entity_decode((string) $comment->body, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
|
||||
while (str_contains($renderedBody, '<') || str_contains($renderedBody, '>') || str_contains($renderedBody, '&lt;')) {
|
||||
while (
|
||||
str_contains($renderedBody, '<') ||
|
||||
str_contains($renderedBody, '>') ||
|
||||
str_contains($renderedBody, '&lt;')
|
||||
) {
|
||||
$decodedBody = html_entity_decode($renderedBody, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
|
||||
if ($decodedBody === $renderedBody) {
|
||||
@@ -36,17 +50,21 @@
|
||||
|
||||
if ($currentTicket !== null) {
|
||||
$renderedBody = strtr($renderedBody, [
|
||||
'{{ ticket.reference }}' => (string) e((string) ($currentTicket->reference ?? '')),
|
||||
'{{ ticket.subject }}' => (string) e((string) ($currentTicket->subject ?? '')),
|
||||
'{{ ticket.status }}' => (string) e((string) ($currentTicket->status?->name ?? '')),
|
||||
'{{ ticket.priority }}' => (string) e((string) ($currentTicket->priority?->name ?? '')),
|
||||
'{{ ticket.category }}' => (string) e((string) ($currentTicket->category?->name ?? '')),
|
||||
'{{ ticket.project }}' => (string) e((string) ($currentTicket->project?->title ?? '')),
|
||||
'{{ ticket.team }}' => (string) e((string) ($currentTicket->team?->name ?? '')),
|
||||
'{{ requester.name }}' => (string) e((string) ($currentTicket->owner?->name ?? $currentTicket->user?->name ?? '')),
|
||||
'{{ agent.name }}' => (string) e((string) ($currentTicket->agent?->name ?? '')),
|
||||
'{{ actor.name }}' => (string) e((string) ($commentUser?->name ?? '')),
|
||||
'{{ ticket.url }}' => (string) e(route('helpdesk.tickets.show', ['ticket' => $currentTicket->getKey()])),
|
||||
'{{ ticket . reference }}' => (string) e((string) ($currentTicket->reference ?? '')),
|
||||
'{{ ticket . subject }}' => (string) e((string) ($currentTicket->subject ?? '')),
|
||||
'{{ ticket . status }}' => (string) e((string) ($currentTicket->status?->name ?? '')),
|
||||
'{{ ticket . priority }}' => (string) e((string) ($currentTicket->priority?->name ?? '')),
|
||||
'{{ ticket . category }}' => (string) e((string) ($currentTicket->category?->name ?? '')),
|
||||
'{{ ticket . project }}' => (string) e((string) ($currentTicket->project?->title ?? '')),
|
||||
'{{ ticket . team }}' => (string) e((string) ($currentTicket->team?->name ?? '')),
|
||||
'{{ requester . name }}' => (string) e(
|
||||
(string) ($currentTicket->owner?->name ?? ($currentTicket->user?->name ?? '')),
|
||||
),
|
||||
'{{ agent . name }}' => (string) e((string) ($currentTicket->agent?->name ?? '')),
|
||||
'{{ actor . name }}' => (string) e((string) ($commentUser?->name ?? '')),
|
||||
'{{ ticket . url }}' => (string) e(
|
||||
route('helpdesk.tickets.show', ['ticket' => $currentTicket->getKey()]),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -54,34 +72,50 @@
|
||||
$renderedBody = preg_replace('/<\s*(script|style)[^>]*>.*?<\s*\/\s*\1\s*>/is', '', $renderedBody) ?: '';
|
||||
$renderedBody = preg_replace('/\s+on[a-z]+\s*=\s*("[^"]*"|\'[^\']*\'|[^\s>]+)/i', '', $renderedBody) ?: '';
|
||||
$renderedBody = preg_replace('/javascript\s*:/i', '', $renderedBody) ?: '';
|
||||
$renderedBody = strip_tags($renderedBody, '<p><br><strong><b><em><i><u><ul><ol><li><blockquote><pre><code><a>');
|
||||
$renderedBody = strip_tags(
|
||||
$renderedBody,
|
||||
'<p><br><strong><b><em><i><u><ul><ol><li><blockquote><pre><code><a>',
|
||||
);
|
||||
$renderedBody = $containsHtml ? $renderedBody : nl2br(e($renderedBody));
|
||||
@endphp
|
||||
|
||||
@if (! $isInternal || $canSeeInternalNotes)
|
||||
<details class="group rounded-2xl border {{ $borderClass }} bg-white shadow-sm transition dark:bg-zinc-900" data-helpdesk-comment-card data-helpdesk-comment-text="{{ e(Str::lower($commentSubject.' '.strip_tags((string) $comment->body).' '.($comment->body_text ?? '').' '.($commentUser?->name ?? '').' '.($comment->unid ?? '').' '.($comment->guid ?? ''))) }}" style="margin-left: {{ min((int) $level, 4) * 1.25 }}rem">
|
||||
@if (!$isInternal || $canSeeInternalNotes)
|
||||
<details class="group rounded-2xl border {{ $borderClass }} bg-white shadow-sm transition dark:bg-zinc-900"
|
||||
data-helpdesk-comment-card
|
||||
data-helpdesk-comment-text="{{ e(Str::lower($commentSubject . ' ' . strip_tags((string) $comment->body) . ' ' . ($comment->body_text ?? '') . ' ' . ($commentUser?->name ?? '') . ' ' . ($comment->unid ?? '') . ' ' . ($comment->guid ?? ''))) }}"
|
||||
style="margin-left: {{ min((int) $level, 4) * 1.25 }}rem">
|
||||
<summary class="flex cursor-pointer list-none items-start gap-3 rounded-2xl p-4 marker:hidden">
|
||||
<span class="mt-1 inline-flex h-12 w-2 shrink-0 rounded-full {{ $railClass }}" aria-hidden="true"></span>
|
||||
<span class="mt-1 inline-flex h-12 w-2 shrink-0 rounded-full {{ $railClass }}"
|
||||
aria-hidden="true"></span>
|
||||
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-zinc-100 text-zinc-700 ring-1 ring-zinc-200 dark:bg-zinc-800 dark:text-zinc-100 dark:ring-zinc-700">
|
||||
<i class="fa-solid {{ $isInternal ? 'fa-note-sticky' : 'fa-envelope' }}" aria-hidden="true"></i>
|
||||
<span
|
||||
class="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-zinc-100 text-zinc-700 ring-1 ring-zinc-200 dark:bg-zinc-800 dark:text-zinc-100 dark:ring-zinc-700">
|
||||
<i class="fa-solid {{ $isInternal ? 'fa-note-sticky' : 'fa-envelope' }}"
|
||||
aria-hidden="true"></i>
|
||||
</span>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="truncate text-base font-extrabold text-zinc-900 dark:text-zinc-100">{{ $commentSubject }}</span>
|
||||
<span class="rounded-full bg-zinc-100 px-2.5 py-1 text-[11px] font-extrabold text-zinc-700 dark:bg-zinc-800 dark:text-zinc-100">{{ $replyCount }} {{ $replyCount === 1 ? 'reply' : 'replies' }}</span>
|
||||
<span
|
||||
class="truncate text-base font-extrabold text-zinc-900 dark:text-zinc-100">{{ $commentSubject }}</span>
|
||||
<span
|
||||
class="rounded-full bg-zinc-100 px-2.5 py-1 text-[11px] font-extrabold text-zinc-700 dark:bg-zinc-800 dark:text-zinc-100">{{ $replyCount }}
|
||||
{{ $replyCount === 1 ? 'reply' : 'replies' }}</span>
|
||||
@if ($commentAttachments->isNotEmpty())
|
||||
<span class="rounded-full bg-emerald-100 px-2.5 py-1 text-[11px] font-extrabold text-emerald-800 dark:bg-emerald-950 dark:text-emerald-100">
|
||||
<i class="fa-solid fa-paperclip mr-1" aria-hidden="true"></i>{{ $commentAttachments->count() }}
|
||||
<span
|
||||
class="rounded-full bg-emerald-100 px-2.5 py-1 text-[11px] font-extrabold text-emerald-800 dark:bg-emerald-950 dark:text-emerald-100">
|
||||
<i class="fa-solid fa-paperclip mr-1"
|
||||
aria-hidden="true"></i>{{ $commentAttachments->count() }}
|
||||
</span>
|
||||
@endif
|
||||
@if ($isInternal)
|
||||
<span class="rounded-full bg-red-600 px-2.5 py-1 text-[11px] font-extrabold uppercase tracking-wide text-white">Internal</span>
|
||||
<span
|
||||
class="rounded-full bg-red-600 px-2.5 py-1 text-[11px] font-extrabold uppercase tracking-wide text-white">Internal</span>
|
||||
@endif
|
||||
</div>
|
||||
<div class="mt-1 flex flex-wrap items-center gap-2 text-xs font-semibold text-zinc-500 dark:text-zinc-400">
|
||||
<div
|
||||
class="mt-1 flex flex-wrap items-center gap-2 text-xs font-semibold text-zinc-500 dark:text-zinc-400">
|
||||
<span>{{ $commentUser?->name ?? 'Unknown user' }}</span>
|
||||
<span aria-hidden="true">•</span>
|
||||
<span>{{ $comment->created_at?->format('d M Y H:i') }}</span>
|
||||
@@ -90,7 +124,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="mt-1 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-zinc-100 text-zinc-600 transition group-open:rotate-90 dark:bg-zinc-800 dark:text-zinc-200">
|
||||
<span
|
||||
class="mt-1 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-zinc-100 text-zinc-600 transition group-open:rotate-90 dark:bg-zinc-800 dark:text-zinc-200">
|
||||
<i class="fa-solid fa-chevron-right text-xs" aria-hidden="true"></i>
|
||||
</span>
|
||||
</summary>
|
||||
@@ -101,16 +136,20 @@
|
||||
</div>
|
||||
|
||||
@if ($commentAttachments->isNotEmpty())
|
||||
<div class="mt-4 rounded-xl border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-950/60">
|
||||
<div class="mb-2 flex items-center gap-2 text-xs font-extrabold uppercase tracking-wide text-zinc-500">
|
||||
<div
|
||||
class="mt-4 rounded-xl border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-950/60">
|
||||
<div
|
||||
class="mb-2 flex items-center gap-2 text-xs font-extrabold uppercase tracking-wide text-zinc-500">
|
||||
<i class="fa-solid fa-paperclip text-blue-500" aria-hidden="true"></i>
|
||||
Attachments
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@foreach ($commentAttachments as $attachment)
|
||||
<a href="{{ route('helpdesk.attachments.download', $attachment->id) }}" target="_blank" class="inline-flex items-center gap-2 rounded-lg border border-blue-200 bg-white px-3 py-2 text-xs font-bold text-blue-700 shadow-sm hover:bg-blue-50 dark:border-blue-900 dark:bg-zinc-900 dark:text-blue-200 dark:hover:bg-blue-950/50">
|
||||
<a href="{{ route('helpdesk.attachments.download', $attachment->id) }}"
|
||||
target="_blank"
|
||||
class="inline-flex items-center gap-2 rounded-lg border border-blue-200 bg-white px-3 py-2 text-xs font-bold text-blue-700 shadow-sm hover:bg-blue-50 dark:border-blue-900 dark:bg-zinc-900 dark:text-blue-200 dark:hover:bg-blue-950/50">
|
||||
<i class="fa-solid fa-thumbtack" aria-hidden="true"></i>
|
||||
{{ $attachment->original_name ?? $attachment->original_filename ?? $attachment->filename ?? $attachment->file_name ?? basename($attachment->path ?? $attachment->file_path ?? '') }}
|
||||
{{ $attachment->original_name ?? ($attachment->original_filename ?? ($attachment->filename ?? ($attachment->file_name ?? basename($attachment->path ?? ($attachment->file_path ?? ''))))) }}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@@ -119,16 +158,21 @@
|
||||
|
||||
@if ($currentTicket !== null)
|
||||
<div class="mt-4 flex flex-wrap justify-end gap-2">
|
||||
<button type="button" dusk="reply-to-comment-{{ $comment->id }}" data-helpdesk-reply-toggle="{{ $replyFormId }}" class="inline-flex items-center justify-center rounded-lg bg-blue-600 px-3 py-2 text-xs font-bold text-white shadow-sm transition hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400/70">
|
||||
<button type="button" dusk="reply-to-comment-{{ $comment->id }}"
|
||||
data-helpdesk-reply-toggle="{{ $replyFormId }}"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-blue-600 px-3 py-2 text-xs font-bold text-white shadow-sm transition hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400/70">
|
||||
<i class="fa-solid fa-reply mr-2" aria-hidden="true"></i>
|
||||
Respond
|
||||
</button>
|
||||
|
||||
@if (auth()->user()?->isMpbaPlatformAdmin())
|
||||
<form method="POST" action="{{ route('helpdesk.tickets.comments.destroy', [$currentTicket, $comment]) }}" onsubmit="return confirm('Delete this response? This will remove it from the visible conversation but keep the replication identity/audit trail.');">
|
||||
<form method="POST"
|
||||
action="{{ route('helpdesk.tickets.comments.destroy', [$currentTicket, $comment]) }}"
|
||||
onsubmit="return confirm('Delete this response? This will remove it from the visible conversation but keep the replication identity/audit trail.');">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" dusk="delete-comment-{{ $comment->id }}" class="inline-flex items-center justify-center rounded-lg bg-red-600 px-3 py-2 text-xs font-bold text-white shadow-sm transition hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-400/70">
|
||||
<button type="submit" dusk="delete-comment-{{ $comment->id }}"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-red-600 px-3 py-2 text-xs font-bold text-white shadow-sm transition hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-400/70">
|
||||
<i class="fa-solid fa-trash-can mr-2" aria-hidden="true"></i>
|
||||
Delete
|
||||
</button>
|
||||
@@ -136,63 +180,98 @@
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<form id="{{ $replyFormId }}" method="POST" action="{{ route('helpdesk.tickets.comments.store', $currentTicket) }}" enctype="multipart/form-data" class="mt-4 hidden rounded-xl border border-red-200 bg-red-50/60 p-4 dark:border-red-900 dark:bg-red-950/20" data-helpdesk-comment-form>
|
||||
<form id="{{ $replyFormId }}" method="POST"
|
||||
action="{{ route('helpdesk.tickets.comments.store', $currentTicket) }}"
|
||||
enctype="multipart/form-data"
|
||||
class="mt-4 hidden rounded-xl border border-red-200 bg-red-50/60 p-4 dark:border-red-900 dark:bg-red-950/20"
|
||||
data-helpdesk-comment-form>
|
||||
@csrf
|
||||
<input type="hidden" name="parent_id" value="{{ $comment->id }}">
|
||||
<div class="mb-3">
|
||||
<label for="reply-subject-{{ $comment->id }}" class="mb-2 block text-sm font-bold text-zinc-700 dark:text-zinc-200">Subject</label>
|
||||
<input id="reply-subject-{{ $comment->id }}" name="subject" value="{{ old('subject', 'RE: '.$commentSubject) }}" class="block w-full rounded-xl border border-zinc-300 bg-white p-3 text-sm font-semibold text-zinc-900 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100">
|
||||
<label for="reply-subject-{{ $comment->id }}"
|
||||
class="mb-2 block text-sm font-bold text-zinc-700 dark:text-zinc-200">Subject</label>
|
||||
<input id="reply-subject-{{ $comment->id }}" name="subject"
|
||||
value="{{ old('subject', 'RE: ' . $commentSubject) }}"
|
||||
class="block w-full rounded-xl border border-zinc-300 bg-white p-3 text-sm font-semibold text-zinc-900 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100">
|
||||
</div>
|
||||
<label for="reply-body-{{ $comment->id }}" class="mb-2 block text-sm font-bold text-zinc-700 dark:text-zinc-200">Response to response</label>
|
||||
<div class="mb-2 flex flex-wrap gap-2" data-helpdesk-html-toolbar="reply-body-{{ $comment->id }}">
|
||||
<button type="button" data-helpdesk-tag="strong" class="rounded-lg border px-2 py-1 text-xs font-black">B</button>
|
||||
<button type="button" data-helpdesk-tag="em" class="rounded-lg border px-2 py-1 text-xs italic">I</button>
|
||||
<button type="button" data-helpdesk-tag="ul" class="rounded-lg border px-2 py-1 text-xs">List</button>
|
||||
<button type="button" data-helpdesk-wrap="<blockquote>" data-helpdesk-wrap-end="</blockquote>" class="rounded-lg border px-2 py-1 text-xs">Quote</button>
|
||||
<button type="button" data-helpdesk-wrap="<code>" data-helpdesk-wrap-end="</code>" class="rounded-lg border px-2 py-1 text-xs">Code</button>
|
||||
<label for="reply-body-{{ $comment->id }}"
|
||||
class="mb-2 block text-sm font-bold text-zinc-700 dark:text-zinc-200">Response to
|
||||
response</label>
|
||||
<div class="mb-2 flex flex-wrap gap-2"
|
||||
data-helpdesk-html-toolbar="reply-body-{{ $comment->id }}">
|
||||
<button type="button" data-helpdesk-tag="strong"
|
||||
class="rounded-lg border px-2 py-1 text-xs font-black">B</button>
|
||||
<button type="button" data-helpdesk-tag="em"
|
||||
class="rounded-lg border px-2 py-1 text-xs italic">I</button>
|
||||
<button type="button" data-helpdesk-tag="ul"
|
||||
class="rounded-lg border px-2 py-1 text-xs">List</button>
|
||||
<button type="button" data-helpdesk-wrap="<blockquote>"
|
||||
data-helpdesk-wrap-end="</blockquote>"
|
||||
class="rounded-lg border px-2 py-1 text-xs">Quote</button>
|
||||
<button type="button" data-helpdesk-wrap="<code>" data-helpdesk-wrap-end="</code>"
|
||||
class="rounded-lg border px-2 py-1 text-xs">Code</button>
|
||||
</div>
|
||||
<textarea id="reply-body-{{ $comment->id }}" dusk="reply-body-{{ $comment->id }}" name="commentBody" rows="4" required class="block w-full rounded-xl border border-zinc-300 bg-white p-3 text-sm text-zinc-900 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100" placeholder="Write a threaded Lotus Notes-style response..."></textarea>
|
||||
<textarea id="reply-body-{{ $comment->id }}" dusk="reply-body-{{ $comment->id }}" name="commentBody" rows="4"
|
||||
required
|
||||
class="block w-full rounded-xl border border-zinc-300 bg-white p-3 text-sm text-zinc-900 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100"
|
||||
placeholder="Write a threaded Lotus Notes-style response..."></textarea>
|
||||
|
||||
@can('update', $currentTicket)
|
||||
@if ($canSeeInternalNotes)
|
||||
<label class="mt-3 flex items-center gap-2 text-sm font-semibold text-zinc-700 dark:text-zinc-200">
|
||||
<input type="checkbox" name="internal" value="1" class="rounded border-zinc-300">
|
||||
<label
|
||||
class="mt-3 flex items-center gap-2 text-sm font-semibold text-zinc-700 dark:text-zinc-200">
|
||||
<input type="checkbox" name="internal" value="1"
|
||||
class="rounded border-zinc-300">
|
||||
MPBA internal note only
|
||||
</label>
|
||||
@endif
|
||||
@endcan
|
||||
|
||||
<details class="mt-4 overflow-hidden rounded-xl border border-zinc-200 bg-white/80 shadow-sm dark:border-zinc-800 dark:bg-zinc-950/50">
|
||||
<summary class="group flex cursor-pointer list-none items-center justify-between gap-3 bg-gradient-to-r from-slate-950 via-blue-950 to-slate-900 px-4 py-3 text-white [&::-webkit-details-marker]:hidden">
|
||||
<details
|
||||
class="mt-4 overflow-hidden rounded-xl border border-zinc-200 bg-white/80 shadow-sm dark:border-zinc-800 dark:bg-zinc-950/50">
|
||||
<summary
|
||||
class="group flex cursor-pointer list-none items-center justify-between gap-3 bg-gradient-to-r from-slate-950 via-blue-950 to-slate-900 px-4 py-3 text-white [&::-webkit-details-marker]:hidden">
|
||||
<span class="flex min-w-0 items-center gap-3">
|
||||
<span class="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-xl border border-blue-400/30 bg-blue-500/15 text-blue-200">
|
||||
<span
|
||||
class="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-xl border border-blue-400/30 bg-blue-500/15 text-blue-200">
|
||||
<i class="fa-solid fa-paperclip text-xs" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span class="min-w-0">
|
||||
<span class="block text-[10px] font-black uppercase tracking-[0.22em] text-blue-200">Attachments</span>
|
||||
<span class="block truncate text-xs font-semibold text-zinc-300">Pin response files.</span>
|
||||
<span
|
||||
class="block text-[10px] font-black uppercase tracking-[0.22em] text-blue-200">Attachments</span>
|
||||
<span class="block truncate text-xs font-semibold text-zinc-300">Pin
|
||||
response files.</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="flex shrink-0 items-center gap-2">
|
||||
<span class="rounded-full border border-white/10 bg-white/10 px-2.5 py-1 text-[10px] font-black text-white" data-helpdesk-file-count>0 files</span>
|
||||
<i class="fa-solid fa-chevron-down text-[10px] text-blue-200 transition group-open:rotate-180" aria-hidden="true"></i>
|
||||
<span
|
||||
class="rounded-full border border-white/10 bg-white/10 px-2.5 py-1 text-[10px] font-black text-white"
|
||||
data-helpdesk-file-count>0 files</span>
|
||||
<i class="fa-solid fa-chevron-down text-[10px] text-blue-200 transition group-open:rotate-180"
|
||||
aria-hidden="true"></i>
|
||||
</span>
|
||||
</summary>
|
||||
<div class="p-4">
|
||||
<div class="flex min-h-[4.5rem] flex-col gap-3 rounded-xl border border-zinc-200 bg-zinc-50/80 p-4 dark:border-zinc-800 dark:bg-zinc-950/70 sm:flex-row sm:items-center sm:justify-between">
|
||||
<span class="text-xs font-semibold text-zinc-500 dark:text-zinc-400" data-helpdesk-file-label>No files attached</span>
|
||||
<div
|
||||
class="flex min-h-[4.5rem] flex-col gap-3 rounded-xl border border-zinc-200 bg-zinc-50/80 p-4 dark:border-zinc-800 dark:bg-zinc-950/70 sm:flex-row sm:items-center sm:justify-between">
|
||||
<span class="text-xs font-semibold text-zinc-500 dark:text-zinc-400"
|
||||
data-helpdesk-file-label>No files attached</span>
|
||||
<label data-helpdesk-file-button>
|
||||
<i class="fa-solid fa-paperclip" aria-hidden="true"></i>
|
||||
<span>Attach files</span>
|
||||
<input name="attachments[]" type="file" multiple class="sr-only" data-helpdesk-file-input>
|
||||
<input name="attachments[]" type="file" multiple class="sr-only"
|
||||
data-helpdesk-file-input>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div class="mt-4 flex justify-end gap-2">
|
||||
<button type="button" data-helpdesk-reply-toggle="{{ $replyFormId }}" class="inline-flex items-center justify-center rounded-lg bg-slate-600 px-3 py-2 text-xs font-bold text-white shadow-sm transition hover:bg-slate-700 focus:outline-none focus:ring-2 focus:ring-slate-400/70">Cancel</button>
|
||||
<button type="submit" dusk="post-reply-{{ $comment->id }}" class="inline-flex items-center justify-center rounded-lg bg-emerald-600 px-3 py-2 text-xs font-bold text-white shadow-sm transition hover:bg-emerald-700 focus:outline-none focus:ring-2 focus:ring-emerald-400/70">Post response</button>
|
||||
<button type="button" data-helpdesk-reply-toggle="{{ $replyFormId }}"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-slate-600 px-3 py-2 text-xs font-bold text-white shadow-sm transition hover:bg-slate-700 focus:outline-none focus:ring-2 focus:ring-slate-400/70">Cancel</button>
|
||||
<button type="submit" dusk="post-reply-{{ $comment->id }}"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-emerald-600 px-3 py-2 text-xs font-bold text-white shadow-sm transition hover:bg-emerald-700 focus:outline-none focus:ring-2 focus:ring-emerald-400/70">Post
|
||||
response</button>
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
{{ $title ?? 'Helpdesk' }}
|
||||
</h1>
|
||||
|
||||
@if(! empty($subtitle))
|
||||
@if (!empty($subtitle))
|
||||
<p class="mt-2 max-w-3xl text-sm text-slate-300">
|
||||
{{ $subtitle }}
|
||||
</p>
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
}
|
||||
@endphp
|
||||
|
||||
@if($helpdeskEnabled)
|
||||
<a href="{{ route('helpdesk.index') }}" wire:navigate class="wms-sidebar-link @if(request()->routeIs('helpdesk.*')) is-active @endif" title="{{ __('Support') }}">
|
||||
@if ($helpdeskEnabled)
|
||||
<a href="{{ route('helpdesk.index') }}" wire:navigate
|
||||
class="wms-sidebar-link @if (request()->routeIs('helpdesk.*')) is-active @endif" title="{{ __('Support') }}">
|
||||
<i class="fa-solid fa-headset wms-sidebar-icon" aria-hidden="true"></i>
|
||||
<span data-wms-sidebar-label>{{ __('Support') }}</span>
|
||||
</a>
|
||||
|
||||
@@ -6,31 +6,21 @@
|
||||
</label>
|
||||
|
||||
<div class="grid grid-cols-[1fr_auto] gap-2">
|
||||
<input
|
||||
id="time_minutes"
|
||||
name="minutes"
|
||||
type="number"
|
||||
min="1"
|
||||
max="1440"
|
||||
value="{{ old('minutes') }}"
|
||||
<input id="time_minutes" name="minutes" type="number" min="1" max="1440" value="{{ old('minutes') }}"
|
||||
placeholder="Minutes"
|
||||
class="block w-full rounded-lg border border-zinc-300 bg-white p-2 text-xs font-semibold text-zinc-900 shadow-sm focus:border-emerald-500 focus:ring-emerald-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100"
|
||||
data-dusk="ticket-time-minutes"
|
||||
>
|
||||
data-dusk="ticket-time-minutes">
|
||||
|
||||
<button type="submit" dusk="log-ticket-time" data-dusk="log-ticket-time" class="inline-flex items-center justify-center rounded-lg bg-emerald-600 px-3 py-2 text-xs font-black text-white shadow-sm transition hover:bg-emerald-700 focus:outline-none focus:ring-2 focus:ring-emerald-400/70">
|
||||
<button type="submit" dusk="log-ticket-time" data-dusk="log-ticket-time"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-emerald-600 px-3 py-2 text-xs font-black text-white shadow-sm transition hover:bg-emerald-700 focus:outline-none focus:ring-2 focus:ring-emerald-400/70">
|
||||
<i class="fa-solid fa-clock mr-2" aria-hidden="true"></i>
|
||||
<span>Log</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<textarea
|
||||
name="description"
|
||||
rows="2"
|
||||
placeholder="Optional work note"
|
||||
<textarea name="description" rows="2" placeholder="Optional work note"
|
||||
class="block w-full rounded-lg border border-zinc-300 bg-white p-2 text-xs font-semibold text-zinc-900 shadow-sm focus:border-emerald-500 focus:ring-emerald-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100"
|
||||
data-dusk="ticket-time-description"
|
||||
>{{ old('description') }}</textarea>
|
||||
data-dusk="ticket-time-description">{{ old('description') }}</textarea>
|
||||
|
||||
@error('minutes')
|
||||
<p class="text-xs font-bold text-red-600 dark:text-red-400">{{ $message }}</p>
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
<x-layouts::app :title="__('Inventory Management')">
|
||||
<div class="container-fluid py-4 module-page module-inventory-management">
|
||||
<div class="container-fluid py-4 module-page module-inventory-management">
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="Inventory Management"
|
||||
<x-wms.page-hero eyebrow="Operations" title="Inventory Management"
|
||||
description="Track inventory movement, stock visibility, pallet quantities, and product availability."
|
||||
icon="fa-solid fa-boxes-stacked"
|
||||
/>
|
||||
icon="fa-solid fa-boxes-stacked" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Stock Overview</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for stock overview.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Stock Overview</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for stock overview.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Pallet Quantity Rules</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for pallet quantity rules.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Pallet Quantity Rules</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for pallet quantity rules.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Inventory Exceptions</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for inventory exceptions.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Inventory Exceptions</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for inventory exceptions.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Product Movement</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for product movement.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Product Movement</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for product movement.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<section class="card border-0 shadow-sm">
|
||||
@@ -53,7 +50,8 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Inventory Management workbench</h2>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.</p>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers,
|
||||
queries, DataTables, or Livewire.</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
@@ -78,26 +76,38 @@
|
||||
<tr>
|
||||
<td>Stock Overview</td>
|
||||
<td>Sample 1</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pallet Quantity Rules</td>
|
||||
<td>Sample 2</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Inventory Exceptions</td>
|
||||
<td>Sample 3</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Product Movement</td>
|
||||
<td>Sample 4</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
<x-layouts::app :title="__('M3 Integration')">
|
||||
<div class="container-fluid py-4 module-page module-m3-integration">
|
||||
<div class="container-fluid py-4 module-page module-m3-integration">
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="M3 Integration"
|
||||
<x-wms.page-hero eyebrow="Operations" title="M3 Integration"
|
||||
description="Observe M3 transactions, delivery methods, close shipment calls, and API exchanges."
|
||||
icon="fa-solid fa-server"
|
||||
/>
|
||||
icon="fa-solid fa-server" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">M3 Transaction Log</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for m3 transaction log.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">M3 Transaction Log</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for m3 transaction log.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">M3 Delivery Methods</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for m3 delivery methods.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">M3 Delivery Methods</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for m3 delivery methods.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Close Shipment API</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for close shipment api.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Close Shipment API</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for close shipment api.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">M3 Error Queue</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for m3 error queue.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">M3 Error Queue</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for m3 error queue.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<section class="card border-0 shadow-sm">
|
||||
@@ -53,7 +50,8 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">M3 Integration workbench</h2>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.</p>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers,
|
||||
queries, DataTables, or Livewire.</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
@@ -78,26 +76,38 @@
|
||||
<tr>
|
||||
<td>M3 Transaction Log</td>
|
||||
<td>Sample 1</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>M3 Delivery Methods</td>
|
||||
<td>Sample 2</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Close Shipment API</td>
|
||||
<td>Sample 3</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>M3 Error Queue</td>
|
||||
<td>Sample 4</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,25 +1,47 @@
|
||||
<x-layouts::app :title="__('Companies')">
|
||||
<div class="space-y-6">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Platform Administration"
|
||||
title="Companies"
|
||||
<x-wms.page-hero eyebrow="Platform Administration" title="Companies"
|
||||
description="Manage tenant companies and keep every company’s users, warehouses and records isolated."
|
||||
icon="fa-solid fa-building"
|
||||
/>
|
||||
icon="fa-solid fa-building" />
|
||||
<div class="rounded-3xl bg-white p-6 shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800">
|
||||
<h2 class="text-lg font-bold text-zinc-900 dark:text-white">Create company</h2>
|
||||
<form method="POST" action="{{ route('platform.companies.store') }}" class="mt-5 grid gap-4 md:grid-cols-4">
|
||||
@csrf
|
||||
<input name="name" placeholder="Company name" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<input name="billing_email" placeholder="billing@example.com" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<select name="status" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950"><option value="active">Active</option><option value="suspended">Suspended</option></select>
|
||||
<button class="rounded-xl bg-zinc-900 px-4 py-2 font-semibold text-white dark:bg-white dark:text-zinc-900">Create</button>
|
||||
<input name="name" placeholder="Company name"
|
||||
class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<input name="billing_email" placeholder="billing@example.com"
|
||||
class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<select name="status" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<option value="active">Active</option>
|
||||
<option value="suspended">Suspended</option>
|
||||
</select>
|
||||
<button
|
||||
class="rounded-xl bg-zinc-900 px-4 py-2 font-semibold text-white dark:bg-white dark:text-zinc-900">Create</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="overflow-x-auto rounded-2xl bg-white shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800">
|
||||
<div
|
||||
class="overflow-x-auto rounded-2xl bg-white shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead><tr class="border-b"><th class="p-4 text-left">Name</th><th class="p-4 text-left">Users</th><th class="p-4 text-left">Warehouses</th><th class="p-4 text-left">Licences</th><th class="p-4 text-left">Status</th></tr></thead>
|
||||
<tbody>@foreach($companies as $company)<tr class="border-b border-zinc-100 dark:border-zinc-800"><td class="p-4 font-semibold">{{ $company->name }}</td><td class="p-4">{{ $company->users_count }}</td><td class="p-4">{{ $company->warehouses_count }}</td><td class="p-4">{{ $company->license_keys_count }}</td><td class="p-4">{{ $company->is_system_company ? 'System' : $company->status }}</td></tr>@endforeach</tbody>
|
||||
<thead>
|
||||
<tr class="border-b">
|
||||
<th class="p-4 text-left">Name</th>
|
||||
<th class="p-4 text-left">Users</th>
|
||||
<th class="p-4 text-left">Warehouses</th>
|
||||
<th class="p-4 text-left">Licences</th>
|
||||
<th class="p-4 text-left">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($companies as $company)
|
||||
<tr class="border-b border-zinc-100 dark:border-zinc-800">
|
||||
<td class="p-4 font-semibold">{{ $company->name }}</td>
|
||||
<td class="p-4">{{ $company->users_count }}</td>
|
||||
<td class="p-4">{{ $company->warehouses_count }}</td>
|
||||
<td class="p-4">{{ $company->license_keys_count }}</td>
|
||||
<td class="p-4">{{ $company->is_system_company ? 'System' : $company->status }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ $companies->links() }}
|
||||
|
||||
@@ -1,54 +1,71 @@
|
||||
<x-layouts.app :title="__('Platform Dashboard')">
|
||||
<div class="space-y-6">
|
||||
<x-wms.page-hero
|
||||
title="Platform Dashboard"
|
||||
<x-wms.page-hero title="Platform Dashboard"
|
||||
description="System-level tenant, licence, pricing and operational oversight for the WMS platform."
|
||||
eyebrow="Platform Administration"
|
||||
/>
|
||||
eyebrow="Platform Administration" />
|
||||
|
||||
<section class="grid gap-4 md:grid-cols-2 xl:grid-cols-5">
|
||||
<article class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-950">
|
||||
<article
|
||||
class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-950">
|
||||
<p class="text-sm font-medium text-slate-500 dark:text-slate-400">Companies</p>
|
||||
<p class="mt-2 text-3xl font-semibold text-slate-950 dark:text-white">{{ number_format($companiesCount ?? 0) }}</p>
|
||||
<p class="mt-2 text-3xl font-semibold text-slate-950 dark:text-white">
|
||||
{{ number_format($companiesCount ?? 0) }}</p>
|
||||
</article>
|
||||
|
||||
<article class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-950">
|
||||
<article
|
||||
class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-950">
|
||||
<p class="text-sm font-medium text-slate-500 dark:text-slate-400">Active</p>
|
||||
<p class="mt-2 text-3xl font-semibold text-emerald-700 dark:text-emerald-300">{{ number_format($activeCompaniesCount ?? 0) }}</p>
|
||||
<p class="mt-2 text-3xl font-semibold text-emerald-700 dark:text-emerald-300">
|
||||
{{ number_format($activeCompaniesCount ?? 0) }}</p>
|
||||
</article>
|
||||
|
||||
<article class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-950">
|
||||
<article
|
||||
class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-950">
|
||||
<p class="text-sm font-medium text-slate-500 dark:text-slate-400">Warehouses</p>
|
||||
<p class="mt-2 text-3xl font-semibold text-slate-950 dark:text-white">{{ number_format($warehousesCount ?? 0) }}</p>
|
||||
<p class="mt-2 text-3xl font-semibold text-slate-950 dark:text-white">
|
||||
{{ number_format($warehousesCount ?? 0) }}</p>
|
||||
</article>
|
||||
|
||||
<article class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-950">
|
||||
<article
|
||||
class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-950">
|
||||
<p class="text-sm font-medium text-slate-500 dark:text-slate-400">Prices</p>
|
||||
<p class="mt-2 text-3xl font-semibold text-slate-950 dark:text-white">{{ number_format($pricesCount ?? 0) }}</p>
|
||||
<p class="mt-2 text-3xl font-semibold text-slate-950 dark:text-white">
|
||||
{{ number_format($pricesCount ?? 0) }}</p>
|
||||
</article>
|
||||
|
||||
<article class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-950">
|
||||
<article
|
||||
class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-950">
|
||||
<p class="text-sm font-medium text-slate-500 dark:text-slate-400">Licences</p>
|
||||
<p class="mt-2 text-3xl font-semibold text-slate-950 dark:text-white">{{ number_format($licensesCount ?? 0) }}</p>
|
||||
<p class="mt-2 text-3xl font-semibold text-slate-950 dark:text-white">
|
||||
{{ number_format($licensesCount ?? 0) }}</p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="grid gap-6 xl:grid-cols-3">
|
||||
<article class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-950 xl:col-span-1">
|
||||
<article
|
||||
class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-950 xl:col-span-1">
|
||||
<h2 class="text-lg font-semibold text-slate-950 dark:text-white">Administration shortcuts</h2>
|
||||
<div class="mt-4 grid gap-3">
|
||||
<a href="{{ route('platform.companies.index') }}" class="rounded-xl border border-slate-200 p-3 text-sm font-semibold hover:bg-slate-50 dark:border-slate-800 dark:hover:bg-slate-900">Manage companies</a>
|
||||
<a href="{{ route('platform.prices.index') }}" class="rounded-xl border border-slate-200 p-3 text-sm font-semibold hover:bg-slate-50 dark:border-slate-800 dark:hover:bg-slate-900">Manage prices</a>
|
||||
<a href="{{ route('platform.pricing.index') }}" class="rounded-xl border border-slate-200 p-3 text-sm font-semibold hover:bg-slate-50 dark:border-slate-800 dark:hover:bg-slate-900">View public pricing</a>
|
||||
<a href="{{ route('platform.companies.index') }}"
|
||||
class="rounded-xl border border-slate-200 p-3 text-sm font-semibold hover:bg-slate-50 dark:border-slate-800 dark:hover:bg-slate-900">Manage
|
||||
companies</a>
|
||||
<a href="{{ route('platform.prices.index') }}"
|
||||
class="rounded-xl border border-slate-200 p-3 text-sm font-semibold hover:bg-slate-50 dark:border-slate-800 dark:hover:bg-slate-900">Manage
|
||||
prices</a>
|
||||
<a href="{{ route('platform.pricing.index') }}"
|
||||
class="rounded-xl border border-slate-200 p-3 text-sm font-semibold hover:bg-slate-50 dark:border-slate-800 dark:hover:bg-slate-900">View
|
||||
public pricing</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-950 xl:col-span-2">
|
||||
<article
|
||||
class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-950 xl:col-span-2">
|
||||
<h2 class="text-lg font-semibold text-slate-950 dark:text-white">Recent companies</h2>
|
||||
<div class="mt-4 overflow-hidden rounded-xl border border-slate-200 dark:border-slate-800">
|
||||
<table class="min-w-full divide-y divide-slate-200 text-sm dark:divide-slate-800">
|
||||
<thead class="bg-slate-50 text-left text-xs uppercase tracking-wide text-slate-500 dark:bg-slate-900 dark:text-slate-400">
|
||||
<thead
|
||||
class="bg-slate-50 text-left text-xs uppercase tracking-wide text-slate-500 dark:bg-slate-900 dark:text-slate-400">
|
||||
<tr>
|
||||
<th class="px-4 py-3">Company</th>
|
||||
<th class="px-4 py-3">Users</th>
|
||||
@@ -60,7 +77,8 @@
|
||||
<tbody class="divide-y divide-slate-200 dark:divide-slate-800">
|
||||
@forelse (($companies ?? collect()) as $company)
|
||||
<tr>
|
||||
<td class="px-4 py-3 font-medium text-slate-950 dark:text-white">{{ $company->name }}</td>
|
||||
<td class="px-4 py-3 font-medium text-slate-950 dark:text-white">
|
||||
{{ $company->name }}</td>
|
||||
<td class="px-4 py-3">{{ $company->users_count ?? 0 }}</td>
|
||||
<td class="px-4 py-3">{{ $company->warehouses_count ?? 0 }}</td>
|
||||
<td class="px-4 py-3">{{ $company->license_keys_count ?? 0 }}</td>
|
||||
@@ -68,7 +86,8 @@
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="px-4 py-6 text-center text-slate-500 dark:text-slate-400">No companies have been created yet.</td>
|
||||
<td colspan="5" class="px-4 py-6 text-center text-slate-500 dark:text-slate-400">
|
||||
No companies have been created yet.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
|
||||
@@ -1,24 +1,59 @@
|
||||
<x-layouts::app :title="__('Licence Keys')">
|
||||
<div class="space-y-6">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Platform Administration"
|
||||
title="Licence Keys"
|
||||
<x-wms.page-hero eyebrow="Platform Administration" title="Licence Keys"
|
||||
description="Issue and monitor company licence keys, limits and platform access plans."
|
||||
icon="fa-solid fa-key"
|
||||
/>
|
||||
@if($plainKey)<div class="rounded-2xl bg-emerald-50 p-4 text-emerald-900 ring-1 ring-emerald-200">New key: <code class="font-bold">{{ $plainKey }}</code></div>@endif
|
||||
icon="fa-solid fa-key" />
|
||||
@if ($plainKey)
|
||||
<div class="rounded-2xl bg-emerald-50 p-4 text-emerald-900 ring-1 ring-emerald-200">New key: <code
|
||||
class="font-bold">{{ $plainKey }}</code></div>
|
||||
@endif
|
||||
<div class="rounded-3xl bg-white p-6 shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800">
|
||||
<h2 class="text-lg font-bold text-zinc-900 dark:text-white">Issue licence key</h2>
|
||||
<form method="POST" action="{{ route('platform.licenses.store') }}" class="mt-5 grid gap-4 md:grid-cols-5">
|
||||
@csrf
|
||||
<select name="company_id" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">@foreach($companies as $company)<option value="{{ $company->id }}">{{ $company->name }}</option>@endforeach</select>
|
||||
<input name="label" placeholder="Licence label" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<select name="plan" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950"><option>standard</option><option>professional</option><option>enterprise</option></select>
|
||||
<input name="max_users" type="number" value="10" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<button class="rounded-xl bg-zinc-900 px-4 py-2 font-semibold text-white dark:bg-white dark:text-zinc-900">Issue</button>
|
||||
<select name="company_id" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
@foreach ($companies as $company)
|
||||
<option value="{{ $company->id }}">{{ $company->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<input name="label" placeholder="Licence label"
|
||||
class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<select name="plan" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<option>standard</option>
|
||||
<option>professional</option>
|
||||
<option>enterprise</option>
|
||||
</select>
|
||||
<input name="max_users" type="number" value="10"
|
||||
class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<button
|
||||
class="rounded-xl bg-zinc-900 px-4 py-2 font-semibold text-white dark:bg-white dark:text-zinc-900">Issue</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="overflow-x-auto rounded-2xl bg-white shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800"><table class="min-w-full text-sm"><thead><tr class="border-b"><th class="p-4 text-left">Company</th><th class="p-4 text-left">Label</th><th class="p-4 text-left">Plan</th><th class="p-4 text-left">Prefix</th><th class="p-4 text-left">Expires</th></tr></thead><tbody>@foreach($licenses as $license)<tr class="border-b border-zinc-100 dark:border-zinc-800"><td class="p-4">{{ $license->company->name }}</td><td class="p-4 font-semibold">{{ $license->label }}</td><td class="p-4">{{ $license->plan }}</td><td class="p-4"><code>{{ $license->key_prefix }}</code></td><td class="p-4">{{ optional($license->expires_at)->toDateString() }}</td></tr>@endforeach</tbody></table></div>
|
||||
<div
|
||||
class="overflow-x-auto rounded-2xl bg-white shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b">
|
||||
<th class="p-4 text-left">Company</th>
|
||||
<th class="p-4 text-left">Label</th>
|
||||
<th class="p-4 text-left">Plan</th>
|
||||
<th class="p-4 text-left">Prefix</th>
|
||||
<th class="p-4 text-left">Expires</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($licenses as $license)
|
||||
<tr class="border-b border-zinc-100 dark:border-zinc-800">
|
||||
<td class="p-4">{{ $license->company->name }}</td>
|
||||
<td class="p-4 font-semibold">{{ $license->label }}</td>
|
||||
<td class="p-4">{{ $license->plan }}</td>
|
||||
<td class="p-4"><code>{{ $license->key_prefix }}</code></td>
|
||||
<td class="p-4">{{ optional($license->expires_at)->toDateString() }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ $licenses->links() }}
|
||||
</div>
|
||||
</x-layouts::app>
|
||||
|
||||
@@ -1,24 +1,58 @@
|
||||
<x-layouts::app :title="__('Payments')">
|
||||
<div class="space-y-6">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Platform Administration"
|
||||
title="Payments and Overrides"
|
||||
<x-wms.page-hero eyebrow="Platform Administration" title="Payments and Overrides"
|
||||
description="Record platform payments, manual overrides and billing status by company."
|
||||
icon="fa-solid fa-credit-card"
|
||||
/>
|
||||
icon="fa-solid fa-credit-card" />
|
||||
<div class="rounded-3xl bg-white p-6 shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800">
|
||||
<h2 class="text-lg font-bold text-zinc-900 dark:text-white">Add payment or override</h2>
|
||||
<form method="POST" action="{{ route('platform.payments.store') }}" class="mt-5 grid gap-4 md:grid-cols-6">
|
||||
@csrf
|
||||
<select name="company_id" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">@foreach($companies as $company)<option value="{{ $company->id }}">{{ $company->name }}</option>@endforeach</select>
|
||||
<input name="amount" type="number" value="0" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<input name="currency" value="gbp" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<select name="status" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950"><option>paid</option><option>pending</option><option>failed</option><option>refunded</option><option>override</option></select>
|
||||
<input name="description" placeholder="Description" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<button class="rounded-xl bg-zinc-900 px-4 py-2 font-semibold text-white dark:bg-white dark:text-zinc-900">Add</button>
|
||||
<select name="company_id" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
@foreach ($companies as $company)
|
||||
<option value="{{ $company->id }}">{{ $company->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<input name="amount" type="number" value="0"
|
||||
class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<input name="currency" value="gbp"
|
||||
class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<select name="status" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<option>paid</option>
|
||||
<option>pending</option>
|
||||
<option>failed</option>
|
||||
<option>refunded</option>
|
||||
<option>override</option>
|
||||
</select>
|
||||
<input name="description" placeholder="Description"
|
||||
class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<button
|
||||
class="rounded-xl bg-zinc-900 px-4 py-2 font-semibold text-white dark:bg-white dark:text-zinc-900">Add</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="overflow-x-auto rounded-2xl bg-white shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800"><table class="min-w-full text-sm"><thead><tr class="border-b"><th class="p-4 text-left">Company</th><th class="p-4 text-left">Amount</th><th class="p-4 text-left">Status</th><th class="p-4 text-left">Description</th></tr></thead><tbody>@foreach($payments as $payment)<tr class="border-b border-zinc-100 dark:border-zinc-800"><td class="p-4">{{ $payment->company->name }}</td><td class="p-4 font-semibold">{{ strtoupper($payment->currency) }} {{ number_format($payment->amount / 100, 2) }}</td><td class="p-4">{{ $payment->status }}</td><td class="p-4">{{ $payment->description }}</td></tr>@endforeach</tbody></table></div>
|
||||
<div
|
||||
class="overflow-x-auto rounded-2xl bg-white shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b">
|
||||
<th class="p-4 text-left">Company</th>
|
||||
<th class="p-4 text-left">Amount</th>
|
||||
<th class="p-4 text-left">Status</th>
|
||||
<th class="p-4 text-left">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($payments as $payment)
|
||||
<tr class="border-b border-zinc-100 dark:border-zinc-800">
|
||||
<td class="p-4">{{ $payment->company->name }}</td>
|
||||
<td class="p-4 font-semibold">{{ strtoupper($payment->currency) }}
|
||||
{{ number_format($payment->amount / 100, 2) }}</td>
|
||||
<td class="p-4">{{ $payment->status }}</td>
|
||||
<td class="p-4">{{ $payment->description }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ $payments->links() }}
|
||||
</div>
|
||||
</x-layouts::app>
|
||||
|
||||
@@ -5,72 +5,120 @@
|
||||
<div class="grid gap-5 lg:grid-cols-2">
|
||||
<div>
|
||||
<label for="name" class="text-sm font-semibold text-zinc-700 dark:text-zinc-200">Name</label>
|
||||
<input id="name" name="name" value="{{ old('name', $price->name) }}" required class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950" placeholder="Basic">
|
||||
@error('name')<p class="mt-1 text-sm text-red-500">{{ $message }}</p>@enderror
|
||||
<input id="name" name="name" value="{{ old('name', $price->name) }}" required
|
||||
class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950"
|
||||
placeholder="Basic">
|
||||
@error('name')
|
||||
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="code" class="text-sm font-semibold text-zinc-700 dark:text-zinc-200">Code</label>
|
||||
<input id="code" name="code" value="{{ old('code', $price->code) }}" class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950" placeholder="basic">
|
||||
@error('code')<p class="mt-1 text-sm text-red-500">{{ $message }}</p>@enderror
|
||||
<input id="code" name="code" value="{{ old('code', $price->code) }}"
|
||||
class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950"
|
||||
placeholder="basic">
|
||||
@error('code')
|
||||
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="strapline" class="text-sm font-semibold text-zinc-700 dark:text-zinc-200">Strapline</label>
|
||||
<input id="strapline" name="strapline" value="{{ old('strapline', $price->strapline) }}" class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950" placeholder="Core warehouse visibility">
|
||||
@error('strapline')<p class="mt-1 text-sm text-red-500">{{ $message }}</p>@enderror
|
||||
<input id="strapline" name="strapline" value="{{ old('strapline', $price->strapline) }}"
|
||||
class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950"
|
||||
placeholder="Core warehouse visibility">
|
||||
@error('strapline')
|
||||
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="badge_label" class="text-sm font-semibold text-zinc-700 dark:text-zinc-200">Badge label</label>
|
||||
<input id="badge_label" name="badge_label" value="{{ old('badge_label', $price->badge_label) }}" class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950" placeholder="Popular">
|
||||
@error('badge_label')<p class="mt-1 text-sm text-red-500">{{ $message }}</p>@enderror
|
||||
<input id="badge_label" name="badge_label" value="{{ old('badge_label', $price->badge_label) }}"
|
||||
class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950"
|
||||
placeholder="Popular">
|
||||
@error('badge_label')
|
||||
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="monthly_amount" class="text-sm font-semibold text-zinc-700 dark:text-zinc-200">Monthly price</label>
|
||||
<input id="monthly_amount" name="monthly_amount" type="number" step="0.01" min="0" value="{{ old('monthly_amount', $price->exists ? number_format($price->monthly_amount / 100, 2, '.', '') : '0.00') }}" required class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950" placeholder="99.00">
|
||||
@error('monthly_amount')<p class="mt-1 text-sm text-red-500">{{ $message }}</p>@enderror
|
||||
<input id="monthly_amount" name="monthly_amount" type="number" step="0.01" min="0"
|
||||
value="{{ old('monthly_amount', $price->exists ? number_format($price->monthly_amount / 100, 2, '.', '') : '0.00') }}"
|
||||
required
|
||||
class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950"
|
||||
placeholder="99.00">
|
||||
@error('monthly_amount')
|
||||
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="annual_amount" class="text-sm font-semibold text-zinc-700 dark:text-zinc-200">Annual price</label>
|
||||
<input id="annual_amount" name="annual_amount" type="number" step="0.01" min="0" value="{{ old('annual_amount', $price->annual_amount !== null ? number_format($price->annual_amount / 100, 2, '.', '') : '') }}" class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950" placeholder="990.00">
|
||||
@error('annual_amount')<p class="mt-1 text-sm text-red-500">{{ $message }}</p>@enderror
|
||||
<input id="annual_amount" name="annual_amount" type="number" step="0.01" min="0"
|
||||
value="{{ old('annual_amount', $price->annual_amount !== null ? number_format($price->annual_amount / 100, 2, '.', '') : '') }}"
|
||||
class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950"
|
||||
placeholder="990.00">
|
||||
@error('annual_amount')
|
||||
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="currency" class="text-sm font-semibold text-zinc-700 dark:text-zinc-200">Currency</label>
|
||||
<input id="currency" name="currency" value="{{ old('currency', $price->currency ?: 'gbp') }}" maxlength="3" required class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm uppercase dark:border-zinc-700 dark:bg-zinc-950" placeholder="gbp">
|
||||
@error('currency')<p class="mt-1 text-sm text-red-500">{{ $message }}</p>@enderror
|
||||
<input id="currency" name="currency" value="{{ old('currency', $price->currency ?: 'gbp') }}" maxlength="3"
|
||||
required
|
||||
class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm uppercase dark:border-zinc-700 dark:bg-zinc-950"
|
||||
placeholder="gbp">
|
||||
@error('currency')
|
||||
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="sort_order" class="text-sm font-semibold text-zinc-700 dark:text-zinc-200">Sort order</label>
|
||||
<input id="sort_order" name="sort_order" type="number" min="0" value="{{ old('sort_order', $price->sort_order ?? 0) }}" required class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950">
|
||||
@error('sort_order')<p class="mt-1 text-sm text-red-500">{{ $message }}</p>@enderror
|
||||
<input id="sort_order" name="sort_order" type="number" min="0"
|
||||
value="{{ old('sort_order', $price->sort_order ?? 0) }}" required
|
||||
class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950">
|
||||
@error('sort_order')
|
||||
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-2">
|
||||
<label for="description" class="text-sm font-semibold text-zinc-700 dark:text-zinc-200">Description</label>
|
||||
<textarea id="description" name="description" rows="3" class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950" placeholder="Short customer-facing description">{{ old('description', $price->description) }}</textarea>
|
||||
@error('description')<p class="mt-1 text-sm text-red-500">{{ $message }}</p>@enderror
|
||||
<textarea id="description" name="description" rows="3"
|
||||
class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950"
|
||||
placeholder="Short customer-facing description">{{ old('description', $price->description) }}</textarea>
|
||||
@error('description')
|
||||
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-2">
|
||||
<label for="features" class="text-sm font-semibold text-zinc-700 dark:text-zinc-200">Features</label>
|
||||
<textarea id="features" name="features" rows="6" class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950" placeholder="One feature per line">{{ $featuresText }}</textarea>
|
||||
@error('features')<p class="mt-1 text-sm text-red-500">{{ $message }}</p>@enderror
|
||||
<textarea id="features" name="features" rows="6"
|
||||
class="mt-2 w-full rounded-xl border-zinc-300 bg-white px-4 py-3 text-sm dark:border-zinc-700 dark:bg-zinc-950"
|
||||
placeholder="One feature per line">{{ $featuresText }}</textarea>
|
||||
@error('features')
|
||||
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-4 lg:col-span-2">
|
||||
<label class="inline-flex items-center gap-2 rounded-xl border border-zinc-200 bg-zinc-50 px-4 py-3 text-sm font-semibold dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<input type="checkbox" name="is_featured" value="1" @checked(old('is_featured', $price->is_featured)) class="rounded border-zinc-300">
|
||||
<label
|
||||
class="inline-flex items-center gap-2 rounded-xl border border-zinc-200 bg-zinc-50 px-4 py-3 text-sm font-semibold dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<input type="checkbox" name="is_featured" value="1" @checked(old('is_featured', $price->is_featured))
|
||||
class="rounded border-zinc-300">
|
||||
Featured price
|
||||
</label>
|
||||
|
||||
<label class="inline-flex items-center gap-2 rounded-xl border border-zinc-200 bg-zinc-50 px-4 py-3 text-sm font-semibold dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<input type="checkbox" name="is_active" value="1" @checked(old('is_active', $price->is_active ?? true)) class="rounded border-zinc-300">
|
||||
<label
|
||||
class="inline-flex items-center gap-2 rounded-xl border border-zinc-200 bg-zinc-50 px-4 py-3 text-sm font-semibold dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<input type="checkbox" name="is_active" value="1" @checked(old('is_active', $price->is_active ?? true))
|
||||
class="rounded border-zinc-300">
|
||||
Active
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<x-layouts::app :title="__('Create Price')">
|
||||
<div class="space-y-6">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Administration"
|
||||
title="Create Price"
|
||||
<x-wms.page-hero eyebrow="Administration" title="Create Price"
|
||||
description="Add a professional pricing card with billing values, feature bullets and visibility controls."
|
||||
icon="fa-solid fa-circle-plus"
|
||||
>
|
||||
<a href="{{ route('platform.prices.index') }}" wire:navigate class="rounded-xl bg-white px-4 py-3 text-sm font-bold text-slate-950 shadow-lg hover:bg-cyan-50">
|
||||
icon="fa-solid fa-circle-plus">
|
||||
<a href="{{ route('platform.prices.index') }}" wire:navigate
|
||||
class="rounded-xl bg-white px-4 py-3 text-sm font-bold text-slate-950 shadow-lg hover:bg-cyan-50">
|
||||
Back to prices
|
||||
</a>
|
||||
</x-wms.page-hero>
|
||||
@@ -16,10 +14,12 @@
|
||||
@include('platform::prices._form')
|
||||
|
||||
<div class="flex flex-wrap justify-end gap-3 border-t border-zinc-100 pt-5 dark:border-zinc-800">
|
||||
<a href="{{ route('platform.prices.index') }}" wire:navigate class="rounded-xl border border-zinc-200 px-5 py-3 text-sm font-semibold text-zinc-700 dark:border-zinc-700 dark:text-zinc-200">
|
||||
<a href="{{ route('platform.prices.index') }}" wire:navigate
|
||||
class="rounded-xl border border-zinc-200 px-5 py-3 text-sm font-semibold text-zinc-700 dark:border-zinc-700 dark:text-zinc-200">
|
||||
Cancel
|
||||
</a>
|
||||
<button type="submit" class="rounded-xl bg-cyan-600 px-5 py-3 text-sm font-bold text-white shadow-lg shadow-cyan-950/20 hover:bg-cyan-500">
|
||||
<button type="submit"
|
||||
class="rounded-xl bg-cyan-600 px-5 py-3 text-sm font-bold text-white shadow-lg shadow-cyan-950/20 hover:bg-cyan-500">
|
||||
Create price
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<x-layouts::app :title="__('Edit Price')">
|
||||
<div class="space-y-6">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Administration"
|
||||
:title="'Edit ' . $price->name"
|
||||
<x-wms.page-hero eyebrow="Administration" :title="'Edit ' . $price->name"
|
||||
description="Update pricing, feature text and card visibility while keeping the platform pricing display consistent."
|
||||
icon="fa-solid fa-pen-to-square"
|
||||
>
|
||||
<a href="{{ route('platform.prices.index') }}" wire:navigate class="rounded-xl bg-white px-4 py-3 text-sm font-bold text-slate-950 shadow-lg hover:bg-cyan-50">
|
||||
icon="fa-solid fa-pen-to-square">
|
||||
<a href="{{ route('platform.prices.index') }}" wire:navigate
|
||||
class="rounded-xl bg-white px-4 py-3 text-sm font-bold text-slate-950 shadow-lg hover:bg-cyan-50">
|
||||
Back to prices
|
||||
</a>
|
||||
</x-wms.page-hero>
|
||||
@@ -17,10 +15,12 @@
|
||||
@include('platform::prices._form')
|
||||
|
||||
<div class="flex flex-wrap justify-end gap-3 border-t border-zinc-100 pt-5 dark:border-zinc-800">
|
||||
<a href="{{ route('platform.prices.index') }}" wire:navigate class="rounded-xl border border-zinc-200 px-5 py-3 text-sm font-semibold text-zinc-700 dark:border-zinc-700 dark:text-zinc-200">
|
||||
<a href="{{ route('platform.prices.index') }}" wire:navigate
|
||||
class="rounded-xl border border-zinc-200 px-5 py-3 text-sm font-semibold text-zinc-700 dark:border-zinc-700 dark:text-zinc-200">
|
||||
Cancel
|
||||
</a>
|
||||
<button type="submit" class="rounded-xl bg-cyan-600 px-5 py-3 text-sm font-bold text-white shadow-lg shadow-cyan-950/20 hover:bg-cyan-500">
|
||||
<button type="submit"
|
||||
class="rounded-xl bg-cyan-600 px-5 py-3 text-sm font-bold text-white shadow-lg shadow-cyan-950/20 hover:bg-cyan-500">
|
||||
Save changes
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
<x-layouts::app :title="__('Platform Prices')">
|
||||
<div class="space-y-6">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Administration"
|
||||
title="Pricing Administration"
|
||||
<x-wms.page-hero eyebrow="Administration" title="Pricing Administration"
|
||||
description="Create, edit and publish the platform pricing cards shown under Platform."
|
||||
icon="fa-solid fa-tags"
|
||||
>
|
||||
<a href="{{ route('platform.prices.create') }}" wire:navigate class="rounded-xl bg-white px-4 py-3 text-sm font-bold text-slate-950 shadow-lg hover:bg-cyan-50">
|
||||
icon="fa-solid fa-tags">
|
||||
<a href="{{ route('platform.prices.create') }}" wire:navigate
|
||||
class="rounded-xl bg-white px-4 py-3 text-sm font-bold text-slate-950 shadow-lg hover:bg-cyan-50">
|
||||
Add price
|
||||
</a>
|
||||
</x-wms.page-hero>
|
||||
@if (session('status'))
|
||||
<div class="rounded-2xl border border-emerald-200 bg-emerald-50 px-5 py-4 text-sm font-semibold text-emerald-800 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-200">
|
||||
<div
|
||||
class="rounded-2xl border border-emerald-200 bg-emerald-50 px-5 py-4 text-sm font-semibold text-emerald-800 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-200">
|
||||
{{ session('status') }}
|
||||
</div>
|
||||
@endif
|
||||
@@ -23,12 +22,16 @@
|
||||
$isAdvanced = $price->code === 'advanced';
|
||||
$headerClass = $isAdvanced
|
||||
? 'from-cyan-500 to-blue-600'
|
||||
: ($isBasic ? 'from-slate-700 to-slate-900' : 'from-indigo-600 to-violet-700');
|
||||
: ($isBasic
|
||||
? 'from-slate-700 to-slate-900'
|
||||
: 'from-indigo-600 to-violet-700');
|
||||
@endphp
|
||||
|
||||
<article class="relative overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm transition hover:-translate-y-0.5 hover:shadow-xl dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<article
|
||||
class="relative overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm transition hover:-translate-y-0.5 hover:shadow-xl dark:border-zinc-800 dark:bg-zinc-900">
|
||||
@if ($price->is_featured)
|
||||
<div class="absolute right-4 top-4 z-10 rounded-full bg-white px-3 py-1 text-xs font-extrabold uppercase tracking-wide text-cyan-700 shadow">
|
||||
<div
|
||||
class="absolute right-4 top-4 z-10 rounded-full bg-white px-3 py-1 text-xs font-extrabold uppercase tracking-wide text-cyan-700 shadow">
|
||||
Featured
|
||||
</div>
|
||||
@endif
|
||||
@@ -42,8 +45,10 @@
|
||||
<h2 class="mt-2 text-2xl font-black tracking-tight">{{ $price->name }}</h2>
|
||||
</div>
|
||||
|
||||
<span class="grid h-11 w-11 place-items-center rounded-2xl bg-white/15 text-lg backdrop-blur">
|
||||
<i class="fa-solid {{ $isBasic ? 'fa-box' : ($isAdvanced ? 'fa-layer-group' : 'fa-crown') }}" aria-hidden="true"></i>
|
||||
<span
|
||||
class="grid h-11 w-11 place-items-center rounded-2xl bg-white/15 text-lg backdrop-blur">
|
||||
<i class="fa-solid {{ $isBasic ? 'fa-box' : ($isAdvanced ? 'fa-layer-group' : 'fa-crown') }}"
|
||||
aria-hidden="true"></i>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -78,19 +83,24 @@
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<div class="mt-6 flex items-center justify-between gap-3 border-t border-zinc-100 pt-4 dark:border-zinc-800">
|
||||
<span class="rounded-full px-3 py-1 text-xs font-bold {{ $price->is_active ? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-950 dark:text-emerald-200' : 'bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300' }}">
|
||||
<div
|
||||
class="mt-6 flex items-center justify-between gap-3 border-t border-zinc-100 pt-4 dark:border-zinc-800">
|
||||
<span
|
||||
class="rounded-full px-3 py-1 text-xs font-bold {{ $price->is_active ? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-950 dark:text-emerald-200' : 'bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300' }}">
|
||||
{{ $price->is_active ? 'Active' : 'Hidden' }}
|
||||
</span>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('platform.prices.edit', $price) }}" wire:navigate class="rounded-lg border border-zinc-200 px-3 py-2 text-sm font-semibold text-zinc-700 hover:border-cyan-400 hover:text-cyan-700 dark:border-zinc-700 dark:text-zinc-200">
|
||||
<a href="{{ route('platform.prices.edit', $price) }}" wire:navigate
|
||||
class="rounded-lg border border-zinc-200 px-3 py-2 text-sm font-semibold text-zinc-700 hover:border-cyan-400 hover:text-cyan-700 dark:border-zinc-700 dark:text-zinc-200">
|
||||
Edit
|
||||
</a>
|
||||
<form method="POST" action="{{ route('platform.prices.destroy', $price) }}" onsubmit="return confirm('Delete this price?');">
|
||||
<form method="POST" action="{{ route('platform.prices.destroy', $price) }}"
|
||||
onsubmit="return confirm('Delete this price?');">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="rounded-lg border border-red-200 px-3 py-2 text-sm font-semibold text-red-600 hover:bg-red-50 dark:border-red-900/70 dark:hover:bg-red-950/40">
|
||||
<button type="submit"
|
||||
class="rounded-lg border border-red-200 px-3 py-2 text-sm font-semibold text-red-600 hover:bg-red-50 dark:border-red-900/70 dark:hover:bg-red-950/40">
|
||||
Delete
|
||||
</button>
|
||||
</form>
|
||||
@@ -99,7 +109,8 @@
|
||||
</div>
|
||||
</article>
|
||||
@empty
|
||||
<article class="rounded-3xl border border-dashed border-zinc-300 bg-white p-6 text-sm text-zinc-500 shadow-sm dark:border-zinc-700 dark:bg-zinc-900 lg:col-span-3">
|
||||
<article
|
||||
class="rounded-3xl border border-dashed border-zinc-300 bg-white p-6 text-sm text-zinc-500 shadow-sm dark:border-zinc-700 dark:bg-zinc-900 lg:col-span-3">
|
||||
No pricing plans have been created yet.
|
||||
</article>
|
||||
@endforelse
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
<x-layouts::app :title="__('Pricing')">
|
||||
<div class="space-y-6">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Platform"
|
||||
title="Pricing"
|
||||
description="Clear platform pricing for Basic, Advanced and Pro access."
|
||||
icon="fa-solid fa-tags"
|
||||
/>
|
||||
<x-wms.page-hero eyebrow="Platform" title="Pricing"
|
||||
description="Clear platform pricing for Basic, Advanced and Pro access." icon="fa-solid fa-tags" />
|
||||
<section class="grid gap-5 lg:grid-cols-3">
|
||||
@forelse ($prices as $price)
|
||||
@php
|
||||
@@ -13,22 +9,30 @@
|
||||
$isAdvanced = $price->code === 'advanced';
|
||||
$headerClass = $isAdvanced
|
||||
? 'from-cyan-500 to-blue-600'
|
||||
: ($isBasic ? 'from-slate-700 to-slate-950' : 'from-indigo-600 to-violet-700');
|
||||
: ($isBasic
|
||||
? 'from-slate-700 to-slate-950'
|
||||
: 'from-indigo-600 to-violet-700');
|
||||
@endphp
|
||||
|
||||
<article class="relative overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm transition hover:-translate-y-0.5 hover:shadow-xl dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<article
|
||||
class="relative overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm transition hover:-translate-y-0.5 hover:shadow-xl dark:border-zinc-800 dark:bg-zinc-900">
|
||||
@if ($price->is_featured)
|
||||
<div class="absolute right-4 top-4 z-10 rounded-full bg-white px-3 py-1 text-xs font-extrabold uppercase tracking-wide text-cyan-700 shadow">{{ $price->badge_label ?: 'Popular' }}</div>
|
||||
<div
|
||||
class="absolute right-4 top-4 z-10 rounded-full bg-white px-3 py-1 text-xs font-extrabold uppercase tracking-wide text-cyan-700 shadow">
|
||||
{{ $price->badge_label ?: 'Popular' }}</div>
|
||||
@endif
|
||||
|
||||
<div class="bg-gradient-to-br {{ $headerClass }} p-6 text-white">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<p class="text-xs font-bold uppercase tracking-[0.24em] text-white/70">{{ $price->badge_label ?: 'Plan' }}</p>
|
||||
<p class="text-xs font-bold uppercase tracking-[0.24em] text-white/70">
|
||||
{{ $price->badge_label ?: 'Plan' }}</p>
|
||||
<h2 class="mt-2 text-3xl font-black tracking-tight">{{ $price->name }}</h2>
|
||||
</div>
|
||||
<span class="grid h-12 w-12 place-items-center rounded-2xl bg-white/15 text-xl backdrop-blur">
|
||||
<i class="fa-solid {{ $isBasic ? 'fa-box' : ($isAdvanced ? 'fa-layer-group' : 'fa-crown') }}" aria-hidden="true"></i>
|
||||
<span
|
||||
class="grid h-12 w-12 place-items-center rounded-2xl bg-white/15 text-xl backdrop-blur">
|
||||
<i class="fa-solid {{ $isBasic ? 'fa-box' : ($isAdvanced ? 'fa-layer-group' : 'fa-crown') }}"
|
||||
aria-hidden="true"></i>
|
||||
</span>
|
||||
</div>
|
||||
<p class="mt-4 text-sm leading-6 text-white/80">{{ $price->strapline }}</p>
|
||||
@@ -36,15 +40,19 @@
|
||||
|
||||
<div class="p-6">
|
||||
<div class="flex items-end gap-2">
|
||||
<span class="text-4xl font-black tracking-tight text-zinc-950 dark:text-white">{{ $price->formatAmount($price->monthly_amount) }}</span>
|
||||
<span
|
||||
class="text-4xl font-black tracking-tight text-zinc-950 dark:text-white">{{ $price->formatAmount($price->monthly_amount) }}</span>
|
||||
<span class="pb-1 text-sm font-semibold text-zinc-500">/ month</span>
|
||||
</div>
|
||||
|
||||
@if ($price->annual_amount)
|
||||
<p class="mt-2 text-sm text-zinc-500">Annual: <span class="font-semibold text-zinc-700 dark:text-zinc-200">{{ $price->formatAmount($price->annual_amount) }}</span></p>
|
||||
<p class="mt-2 text-sm text-zinc-500">Annual: <span
|
||||
class="font-semibold text-zinc-700 dark:text-zinc-200">{{ $price->formatAmount($price->annual_amount) }}</span>
|
||||
</p>
|
||||
@endif
|
||||
|
||||
<p class="mt-4 min-h-12 text-sm leading-6 text-zinc-600 dark:text-zinc-300">{{ $price->description }}</p>
|
||||
<p class="mt-4 min-h-12 text-sm leading-6 text-zinc-600 dark:text-zinc-300">
|
||||
{{ $price->description }}</p>
|
||||
|
||||
<ul class="mt-5 space-y-3 text-sm text-zinc-700 dark:text-zinc-200">
|
||||
@foreach ($price->featureRows() as $feature)
|
||||
@@ -57,9 +65,11 @@
|
||||
</div>
|
||||
</article>
|
||||
@empty
|
||||
<article class="rounded-3xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<article
|
||||
class="rounded-3xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<h2 class="text-xl font-bold">No active prices</h2>
|
||||
<p class="mt-2 text-sm text-zinc-500">Add active pricing records from Administration to populate this page.</p>
|
||||
<p class="mt-2 text-sm text-zinc-500">Add active pricing records from Administration to populate
|
||||
this page.</p>
|
||||
</article>
|
||||
@endforelse
|
||||
</section>
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
<x-layouts::app :title="__('Pricing')">
|
||||
<div class="space-y-6">
|
||||
<x-wms.page-hero
|
||||
eyebrow="MPBA WMS"
|
||||
title="Pricing"
|
||||
<x-wms.page-hero eyebrow="MPBA WMS" title="Pricing"
|
||||
description="Platform pricing workspace for plan visibility and commercial administration."
|
||||
:stats="[
|
||||
['label' => 'Status', 'value' => 'Ready', 'icon' => 'fa-solid fa-circle-check'],
|
||||
]"
|
||||
/>
|
||||
:stats="[['label' => 'Status', 'value' => 'Ready', 'icon' => 'fa-solid fa-circle-check']]" />
|
||||
|
||||
<section class="rounded-3xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<h2 class="text-lg font-bold text-zinc-900 dark:text-white">Pricing workspace</h2>
|
||||
<p class="mt-2 text-sm text-zinc-500 dark:text-zinc-400">Use the Platform pricing pages to maintain Basic, Advanced and Pro plan cards.</p>
|
||||
<p class="mt-2 text-sm text-zinc-500 dark:text-zinc-400">Use the Platform pricing pages to maintain Basic,
|
||||
Advanced and Pro plan cards.</p>
|
||||
</section>
|
||||
</div>
|
||||
</x-layouts::app>
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Module Pricing</title>
|
||||
|
||||
{{-- Laravel Mix - CSS File --}}
|
||||
{{-- <link rel="stylesheet" href="{{ mix('css/pricing.css') }}"> --}}
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Module Pricing</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);" aria-hidden="true"></div>
|
||||
@yield('content')
|
||||
{{-- Laravel Mix - CSS File --}}
|
||||
{{-- <link rel="stylesheet" href="{{ mix('css/pricing.css') }}"> --}}
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
|
||||
aria-hidden="true"></div>
|
||||
@yield('content')
|
||||
|
||||
{{-- Laravel Mix - JS File --}}
|
||||
{{-- <script src="{{ mix('js/pricing.js') }}"></script> --}}
|
||||
</body>
|
||||
|
||||
{{-- Laravel Mix - JS File --}}
|
||||
{{-- <script src="{{ mix('js/pricing.js') }}"></script> --}}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
<x-layouts::app :title="__('Replenishment')">
|
||||
<div class="container-fluid py-4 module-page module-replenishment">
|
||||
<div class="container-fluid py-4 module-page module-replenishment">
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="Replenishment"
|
||||
<x-wms.page-hero eyebrow="Operations" title="Replenishment"
|
||||
description="Configure replenishment thresholds, review stock gaps, and monitor refill activity."
|
||||
icon="fa-solid fa-arrows-rotate"
|
||||
/>
|
||||
icon="fa-solid fa-arrows-rotate" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Replenishment Rules</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for replenishment rules.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Replenishment Rules</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for replenishment rules.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Minimum Stock</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for minimum stock.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Minimum Stock</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for minimum stock.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Suggested Transfers</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for suggested transfers.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Suggested Transfers</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for suggested transfers.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Exception Queue</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for exception queue.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Exception Queue</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for exception queue.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<section class="card border-0 shadow-sm">
|
||||
@@ -53,7 +50,8 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Replenishment workbench</h2>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.</p>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers,
|
||||
queries, DataTables, or Livewire.</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
@@ -78,26 +76,38 @@
|
||||
<tr>
|
||||
<td>Replenishment Rules</td>
|
||||
<td>Sample 1</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Minimum Stock</td>
|
||||
<td>Sample 2</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Suggested Transfers</td>
|
||||
<td>Sample 3</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Exception Queue</td>
|
||||
<td>Sample 4</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
<x-layouts::app :title="__('Reporting')">
|
||||
<div class="container-fluid py-4 module-page module-reporting">
|
||||
<div class="container-fluid py-4 module-page module-reporting">
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="Reporting"
|
||||
<x-wms.page-hero eyebrow="Operations" title="Reporting"
|
||||
description="Provide operational reports for closed orders, user efficiency, and transaction history."
|
||||
icon="fa-solid fa-chart-column"
|
||||
/>
|
||||
icon="fa-solid fa-chart-column" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Closed Order Lines</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for closed order lines.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Closed Order Lines</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for closed order lines.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Daily User Efficiency</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for daily user efficiency.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Daily User Efficiency</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for daily user efficiency.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Export Centre</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for export centre.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Export Centre</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for export centre.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Report Filters</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for report filters.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Report Filters</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for report filters.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<section class="card border-0 shadow-sm">
|
||||
@@ -53,7 +50,8 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Reporting workbench</h2>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.</p>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers,
|
||||
queries, DataTables, or Livewire.</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
@@ -78,26 +76,38 @@
|
||||
<tr>
|
||||
<td>Closed Order Lines</td>
|
||||
<td>Sample 1</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Daily User Efficiency</td>
|
||||
<td>Sample 2</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Export Centre</td>
|
||||
<td>Sample 3</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Report Filters</td>
|
||||
<td>Sample 4</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
@vite([
|
||||
'resources/css/app.css',
|
||||
'resources/js/app.js',
|
||||
])
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
<div class="hd-helpdesk-surface space-y-6" dusk="helpdesk-ticket-show">
|
||||
<div class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="bg-gradient-to-br from-slate-950 via-slate-900 to-blue-950 px-6 py-7 text-white">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
@@ -13,14 +11,17 @@
|
||||
<flux:badge color="{{ $ticket->priority->color }}">{{ $ticket->priority->name }}</flux:badge>
|
||||
</div>
|
||||
<h1 class="text-3xl font-semibold tracking-tight">{{ $ticket->subject }}</h1>
|
||||
<p class="mt-2 text-sm text-blue-100/90">Raised by {{ $ticket->owner?->name ?? 'Unknown user' }} · Assigned to {{ $ticket->agent?->name ?? 'Unassigned' }}</p>
|
||||
<p class="mt-2 text-sm text-blue-100/90">Raised by {{ $ticket->owner?->name ?? 'Unknown user' }} ·
|
||||
Assigned to {{ $ticket->agent?->name ?? 'Unassigned' }}</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a href="{{ route('helpdesk.tickets.index') }}" wire:navigate class="inline-flex items-center rounded-xl bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">
|
||||
<a href="{{ route('helpdesk.tickets.index') }}" wire:navigate
|
||||
class="inline-flex items-center rounded-xl bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">
|
||||
<i class="fa-solid fa-arrow-left mr-2" aria-hidden="true"></i> Back
|
||||
</a>
|
||||
@can('update', $ticket)
|
||||
<a href="{{ route('helpdesk.tickets.edit', $ticket) }}" wire:navigate class="inline-flex items-center rounded-xl bg-emerald-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-emerald-700">
|
||||
<a href="{{ route('helpdesk.tickets.edit', $ticket) }}" wire:navigate
|
||||
class="inline-flex items-center rounded-xl bg-emerald-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-emerald-700">
|
||||
<i class="fa-solid fa-pen-to-square mr-2" aria-hidden="true"></i> Edit
|
||||
</a>
|
||||
@endcan
|
||||
@@ -31,56 +32,72 @@
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-3">
|
||||
<div class="space-y-6 lg:col-span-2">
|
||||
<article class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<article
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="prose max-w-none dark:prose-invert">
|
||||
{!! $ticket->body !!}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@if ($ticket->attachments->isNotEmpty())
|
||||
<div class="overflow-hidden rounded-2xl border border-blue-200 bg-white shadow-sm dark:border-blue-900/60 dark:bg-zinc-900">
|
||||
<div class="bg-gradient-to-r from-blue-700 via-sky-600 to-emerald-600 px-6 py-4 text-white">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<h2 class="flex items-center gap-2 text-base font-extrabold text-white drop-shadow-sm">
|
||||
<span class="inline-flex h-9 w-9 items-center justify-center rounded-xl bg-white/15 ring-1 ring-white/25">
|
||||
<i class="fa-solid fa-paperclip" aria-hidden="true"></i>
|
||||
</span>
|
||||
Ticket attachments
|
||||
</h2>
|
||||
<p class="mt-1 text-sm font-medium text-blue-50/90">
|
||||
Supporting screenshots, documents and logs linked to this ticket.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl border border-blue-200 bg-white shadow-sm dark:border-blue-900/60 dark:bg-zinc-900">
|
||||
<div class="bg-gradient-to-r from-blue-700 via-sky-600 to-emerald-600 px-6 py-4 text-white">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<h2 class="flex items-center gap-2 text-base font-extrabold text-white drop-shadow-sm">
|
||||
<span
|
||||
class="inline-flex h-9 w-9 items-center justify-center rounded-xl bg-white/15 ring-1 ring-white/25">
|
||||
<i class="fa-solid fa-paperclip" aria-hidden="true"></i>
|
||||
</span>
|
||||
Ticket attachments
|
||||
</h2>
|
||||
<p class="mt-1 text-sm font-medium text-blue-50/90">
|
||||
Supporting screenshots, documents and logs linked to this ticket.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<span class="inline-flex items-center gap-2 rounded-full bg-emerald-100 px-3 py-1 text-xs font-extrabold text-emerald-800 shadow-sm ring-1 ring-white/70">
|
||||
<span class="inline-flex h-2 w-2 rounded-full bg-emerald-500"></span>{{ $ticket->attachments->count() }} file(s)
|
||||
</span>
|
||||
<span
|
||||
class="inline-flex items-center gap-2 rounded-full bg-emerald-100 px-3 py-1 text-xs font-extrabold text-emerald-800 shadow-sm ring-1 ring-white/70">
|
||||
<span
|
||||
class="inline-flex h-2 w-2 rounded-full bg-emerald-500"></span>{{ $ticket->attachments->count() }}
|
||||
file(s)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 p-6 sm:grid-cols-2">
|
||||
@foreach ($ticket->attachments as $attachment)
|
||||
<a href="{{ route('helpdesk.attachments.download', $attachment->id) }}" target="_blank"
|
||||
class="rounded-xl border border-zinc-200 p-3 text-sm hover:bg-zinc-50 dark:border-zinc-700 dark:hover:bg-zinc-800">
|
||||
<div class="font-medium text-zinc-900 dark:text-zinc-100">
|
||||
{{ $attachment->original_name ?? ($attachment->original_filename ?? ($attachment->filename ?? ($attachment->file_name ?? basename($attachment->path ?? ($attachment->file_path ?? ''))))) }}
|
||||
</div>
|
||||
<div class="text-xs text-zinc-500">
|
||||
{{ Number::fileSize($attachment->size ?? ($attachment->size_bytes ?? 0)) }}</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 p-6 sm:grid-cols-2">
|
||||
@foreach ($ticket->attachments as $attachment)
|
||||
<a href="{{ route('helpdesk.attachments.download', $attachment->id) }}" target="_blank" class="rounded-xl border border-zinc-200 p-3 text-sm hover:bg-zinc-50 dark:border-zinc-700 dark:hover:bg-zinc-800">
|
||||
<div class="font-medium text-zinc-900 dark:text-zinc-100">{{ $attachment->original_name ?? $attachment->original_filename ?? $attachment->filename ?? $attachment->file_name ?? basename($attachment->path ?? $attachment->file_path ?? '') }}</div>
|
||||
<div class="text-xs text-zinc-500">{{ Number::fileSize($attachment->size ?? $attachment->size_bytes ?? 0) }}</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-lg font-semibold text-zinc-900 dark:text-white">Conversation</h2>
|
||||
|
||||
@if ($ticket->comments->isNotEmpty())
|
||||
@include('helpdesk::partials.comment-thread', ['comments' => $ticket->comments, 'level' => 0])
|
||||
@include('helpdesk::partials.comment-thread', [
|
||||
'comments' => $ticket->comments,
|
||||
'level' => 0,
|
||||
])
|
||||
@else
|
||||
<div class="rounded-2xl border border-dashed border-zinc-300 p-8 text-center text-zinc-500 dark:border-zinc-700">No responses yet.</div>
|
||||
<div
|
||||
class="rounded-2xl border border-dashed border-zinc-300 p-8 text-center text-zinc-500 dark:border-zinc-700">
|
||||
No responses yet.</div>
|
||||
@endif
|
||||
</section>
|
||||
|
||||
<form wire:submit="addComment" class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<form wire:submit="addComment"
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<h2 class="mb-4 text-base font-semibold text-zinc-900 dark:text-white">Add reply</h2>
|
||||
@if ($cannedResponses->isNotEmpty())
|
||||
<div class="mb-4 grid gap-3 md:grid-cols-[1fr_auto]">
|
||||
@@ -91,12 +108,16 @@
|
||||
@endforeach
|
||||
</flux:select>
|
||||
<div class="flex items-end">
|
||||
<flux:button type="button" wire:click="applyCannedResponse" variant="ghost" icon="document-duplicate" class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Insert response</flux:button>
|
||||
<flux:button type="button" wire:click="applyCannedResponse" variant="ghost"
|
||||
icon="document-duplicate"
|
||||
class="!bg-blue-600 !text-white hover:!bg-blue-700 focus:!ring-blue-400/60">Insert
|
||||
response</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<x-helpdesk::html-editor dusk="ticket-comment-editor" wire:model="commentBody" label="Comment" rows="5" placeholder="Write a professional response..." />
|
||||
<x-helpdesk::html-editor dusk="ticket-comment-editor" wire:model="commentBody" label="Comment"
|
||||
rows="5" placeholder="Write a professional response..." />
|
||||
|
||||
@can('update', $ticket)
|
||||
<div class="mt-4">
|
||||
@@ -106,12 +127,17 @@
|
||||
|
||||
<div class="mt-4">
|
||||
<label class="mb-2 block text-sm font-medium text-zinc-700 dark:text-zinc-200">Attach files</label>
|
||||
<input wire:model="attachments" type="file" multiple class="block w-full rounded-lg border border-zinc-300 bg-white p-2 text-sm dark:border-zinc-700 dark:bg-zinc-950" />
|
||||
@error('attachments.*') <div class="mt-2 text-sm text-red-600">{{ $message }}</div> @enderror
|
||||
<input wire:model="attachments" type="file" multiple
|
||||
class="block w-full rounded-lg border border-zinc-300 bg-white p-2 text-sm dark:border-zinc-700 dark:bg-zinc-950" />
|
||||
@error('attachments.*')
|
||||
<div class="mt-2 text-sm text-red-600">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mt-5 flex justify-end">
|
||||
<flux:button dusk="post-comment" type="submit" variant="primary" icon="chat-bubble-left-right" class="!bg-emerald-600 !text-white hover:!bg-emerald-700 focus:!ring-emerald-400/60" wire:loading.attr="disabled">
|
||||
<flux:button dusk="post-comment" type="submit" variant="primary" icon="chat-bubble-left-right"
|
||||
class="!bg-emerald-600 !text-white hover:!bg-emerald-700 focus:!ring-emerald-400/60"
|
||||
wire:loading.attr="disabled">
|
||||
<span wire:loading.remove>Post reply</span>
|
||||
<span wire:loading>Posting...</span>
|
||||
</flux:button>
|
||||
@@ -120,143 +146,224 @@
|
||||
</div>
|
||||
|
||||
<aside class="space-y-6">
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<h2 class="mb-4 text-base font-semibold text-zinc-900 dark:text-white">Ticket details</h2>
|
||||
<dl class="space-y-3 text-sm">
|
||||
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Category</dt><dd>{{ $ticket->category?->name ?? 'None' }}</dd></div>
|
||||
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Project</dt><dd>{{ $ticket->project?->title ?? 'None' }}</dd></div>
|
||||
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Product/service</dt><dd>{{ $ticket->product?->name ?? 'None' }}</dd></div>
|
||||
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Team</dt><dd>{{ $ticket->team?->name ?? 'None' }}</dd></div>
|
||||
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Created</dt><dd>{{ $ticket->created_at->format('d M Y H:i') }}</dd></div>
|
||||
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Due</dt><dd>{{ $ticket->due_at?->format('d M Y') ?? 'No due date' }}</dd></div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<dt class="text-zinc-500">Category</dt>
|
||||
<dd>{{ $ticket->category?->name ?? 'None' }}</dd>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<dt class="text-zinc-500">Project</dt>
|
||||
<dd>{{ $ticket->project?->title ?? 'None' }}</dd>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<dt class="text-zinc-500">Product/service</dt>
|
||||
<dd>{{ $ticket->product?->name ?? 'None' }}</dd>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<dt class="text-zinc-500">Team</dt>
|
||||
<dd>{{ $ticket->team?->name ?? 'None' }}</dd>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<dt class="text-zinc-500">Created</dt>
|
||||
<dd>{{ $ticket->created_at->format('d M Y H:i') }}</dd>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<dt class="text-zinc-500">Due</dt>
|
||||
<dd>{{ $ticket->due_at?->format('d M Y') ?? 'No due date' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<h2 class="mb-4 text-base font-semibold text-zinc-900 dark:text-white">SLA metrics</h2>
|
||||
<div class="grid gap-3 text-sm">
|
||||
<div class="rounded-xl border {{ $ticket->sla_first_response_breached ? 'border-red-300 bg-red-50 text-red-900 dark:border-red-900 dark:bg-red-950 dark:text-red-100' : 'border-emerald-200 bg-emerald-50 text-emerald-900 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-100' }} p-3">
|
||||
<div
|
||||
class="rounded-xl border {{ $ticket->sla_first_response_breached ? 'border-red-300 bg-red-50 text-red-900 dark:border-red-900 dark:bg-red-950 dark:text-red-100' : 'border-emerald-200 bg-emerald-50 text-emerald-900 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-100' }} p-3">
|
||||
<div class="text-xs font-semibold uppercase tracking-wide">First response</div>
|
||||
<div class="mt-1">{{ $ticket->first_response_at?->format('d M Y H:i') ?? 'Awaiting response' }}</div>
|
||||
<div class="mt-1 text-xs opacity-75">Due {{ $ticket->sla_first_response_due_at?->format('d M Y H:i') ?? 'Not set' }}</div>
|
||||
<div class="mt-1">
|
||||
{{ $ticket->first_response_at?->format('d M Y H:i') ?? 'Awaiting response' }}</div>
|
||||
<div class="mt-1 text-xs opacity-75">Due
|
||||
{{ $ticket->sla_first_response_due_at?->format('d M Y H:i') ?? 'Not set' }}</div>
|
||||
</div>
|
||||
<div class="rounded-xl border {{ $ticket->sla_resolution_breached ? 'border-red-300 bg-red-50 text-red-900 dark:border-red-900 dark:bg-red-950 dark:text-red-100' : 'border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-900 dark:bg-blue-950 dark:text-blue-100' }} p-3">
|
||||
<div
|
||||
class="rounded-xl border {{ $ticket->sla_resolution_breached ? 'border-red-300 bg-red-50 text-red-900 dark:border-red-900 dark:bg-red-950 dark:text-red-100' : 'border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-900 dark:bg-blue-950 dark:text-blue-100' }} p-3">
|
||||
<div class="text-xs font-semibold uppercase tracking-wide">Resolution</div>
|
||||
<div class="mt-1">{{ $ticket->completed_at?->format('d M Y H:i') ?? 'Open' }}</div>
|
||||
<div class="mt-1 text-xs opacity-75">Due {{ $ticket->sla_resolution_due_at?->format('d M Y H:i') ?? 'Not set' }}</div>
|
||||
<div class="mt-1 text-xs opacity-75">Due
|
||||
{{ $ticket->sla_resolution_due_at?->format('d M Y H:i') ?? 'Not set' }}</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2 text-center">
|
||||
<div class="rounded-xl border border-zinc-200 p-3 dark:border-zinc-700"><div class="text-xl font-semibold">{{ $ticket->public_replies_count }}</div><div class="text-xs text-zinc-500">Public replies</div></div>
|
||||
<div class="rounded-xl border border-zinc-200 p-3 dark:border-zinc-700"><div class="text-xl font-semibold">{{ $ticket->time_spent_minutes }}</div><div class="text-xs text-zinc-500">Minutes logged</div></div>
|
||||
<div class="rounded-xl border border-zinc-200 p-3 dark:border-zinc-700">
|
||||
<div class="text-xl font-semibold">{{ $ticket->public_replies_count }}</div>
|
||||
<div class="text-xs text-zinc-500">Public replies</div>
|
||||
</div>
|
||||
<div class="rounded-xl border border-zinc-200 p-3 dark:border-zinc-700">
|
||||
<div class="text-xl font-semibold">{{ $ticket->time_spent_minutes }}</div>
|
||||
<div class="text-xs text-zinc-500">Minutes logged</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<h2 class="mb-4 text-base font-semibold text-zinc-900 dark:text-white">Requester profile</h2>
|
||||
<dl class="space-y-3 text-sm">
|
||||
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Name</dt><dd>{{ $ticket->owner?->name ?? 'Unknown' }}</dd></div>
|
||||
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Email</dt><dd class="truncate">{{ $ticket->owner?->email ?? 'None' }}</dd></div>
|
||||
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Company</dt><dd>{{ $ticket->owner?->company?->name ?? 'Current company' }}</dd></div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<dt class="text-zinc-500">Name</dt>
|
||||
<dd>{{ $ticket->owner?->name ?? 'Unknown' }}</dd>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<dt class="text-zinc-500">Email</dt>
|
||||
<dd class="truncate">{{ $ticket->owner?->email ?? 'None' }}</dd>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<dt class="text-zinc-500">Company</dt>
|
||||
<dd>{{ $ticket->owner?->company?->name ?? 'Current company' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
@if ($ticket->tags->isNotEmpty())
|
||||
<div class="mt-4 flex flex-wrap gap-2">
|
||||
@foreach ($ticket->tags as $tag)
|
||||
<span class="rounded-full px-2 py-1 text-xs font-semibold text-white" style="background-color: {{ \Modules\Helpdesk\Support\Color::css($tag->color) }}">{{ $tag->name }}</span>
|
||||
<span class="rounded-full px-2 py-1 text-xs font-semibold text-white"
|
||||
style="background-color: {{ \Modules\Helpdesk\Support\Color::css($tag->color) }}">{{ $tag->name }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if ($ticket->checklistItems->isNotEmpty())
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
@php($completedChecks = $ticket->checklistItems->where('is_completed', true)->count())
|
||||
<div class="mb-4 flex items-center justify-between gap-3">
|
||||
<h2 class="text-base font-semibold text-zinc-900 dark:text-white">Checklist</h2>
|
||||
<flux:badge color="blue">{{ $completedChecks }}/{{ $ticket->checklistItems->count() }}</flux:badge>
|
||||
<flux:badge color="blue">{{ $completedChecks }}/{{ $ticket->checklistItems->count() }}
|
||||
</flux:badge>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
@foreach ($ticket->checklistItems as $item)
|
||||
<label class="flex items-start gap-3 rounded-xl border border-zinc-200 p-3 text-sm dark:border-zinc-700">
|
||||
<input type="checkbox" wire:click="toggleChecklistItem({{ $item->id }})" @checked($item->is_completed) class="mt-1 rounded border-zinc-300">
|
||||
<span class="{{ $item->is_completed ? 'text-zinc-500 line-through' : 'text-zinc-800 dark:text-zinc-100' }}">{{ $item->title }}</span>
|
||||
<label
|
||||
class="flex items-start gap-3 rounded-xl border border-zinc-200 p-3 text-sm dark:border-zinc-700">
|
||||
<input type="checkbox" wire:click="toggleChecklistItem({{ $item->id }})"
|
||||
@checked($item->is_completed) class="mt-1 rounded border-zinc-300">
|
||||
<span
|
||||
class="{{ $item->is_completed ? 'text-zinc-500 line-through' : 'text-zinc-800 dark:text-zinc-100' }}">{{ $item->title }}</span>
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<h2 class="mb-4 text-base font-semibold text-zinc-900 dark:text-white">Git commits</h2>
|
||||
<div class="space-y-2">
|
||||
@forelse ($ticket->gitCommits as $commit)
|
||||
<a href="{{ $commit->url ?: '#' }}" target="_blank" class="block rounded-xl border border-zinc-100 p-3 dark:border-zinc-800">
|
||||
<div class="font-mono text-xs text-zinc-500">{{ $commit->short_hash }} · {{ $commit->repository?->name }}</div>
|
||||
<div class="mt-1 text-sm font-medium text-zinc-900 dark:text-zinc-100">{{ $commit->message }}</div>
|
||||
<a href="{{ $commit->url ?: '#' }}" target="_blank"
|
||||
class="block rounded-xl border border-zinc-100 p-3 dark:border-zinc-800">
|
||||
<div class="font-mono text-xs text-zinc-500">{{ $commit->short_hash }} ·
|
||||
{{ $commit->repository?->name }}</div>
|
||||
<div class="mt-1 text-sm font-medium text-zinc-900 dark:text-zinc-100">
|
||||
{{ $commit->message }}</div>
|
||||
</a>
|
||||
@empty
|
||||
<div class="rounded-xl border border-dashed border-zinc-300 p-4 text-center text-sm text-zinc-500 dark:border-zinc-700">No commits linked.</div>
|
||||
<div
|
||||
class="rounded-xl border border-dashed border-zinc-300 p-4 text-center text-sm text-zinc-500 dark:border-zinc-700">
|
||||
No commits linked.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@can('update', $ticket)
|
||||
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div class="mb-5 rounded-2xl bg-gradient-to-r from-blue-700 via-indigo-700 to-violet-700 px-4 py-4 text-white shadow-sm">
|
||||
<div
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<div
|
||||
class="mb-5 rounded-2xl bg-gradient-to-r from-blue-700 via-indigo-700 to-violet-700 px-4 py-4 text-white shadow-sm">
|
||||
<p class="text-xs font-black uppercase tracking-[0.24em] text-blue-100">Ticket controls</p>
|
||||
<h2 class="mt-1 text-lg font-black tracking-tight">Assignment, status and priority</h2>
|
||||
<p class="mt-1 text-sm font-semibold text-blue-100/90">Use the explicit update buttons below to save each change.</p>
|
||||
<p class="mt-1 text-sm font-semibold text-blue-100/90">Use the explicit update buttons below to
|
||||
save each change.</p>
|
||||
</div>
|
||||
|
||||
@if (session('ticket-action-updated'))
|
||||
<div class="mb-5 rounded-xl border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm font-bold text-emerald-800 dark:border-emerald-800 dark:bg-emerald-950 dark:text-emerald-100">
|
||||
<div
|
||||
class="mb-5 rounded-xl border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm font-bold text-emerald-800 dark:border-emerald-800 dark:bg-emerald-950 dark:text-emerald-100">
|
||||
{{ session('ticket-action-updated') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('helpdesk.tickets.agent.update', $ticket) }}" class="mb-5 space-y-3">
|
||||
<form method="POST" action="{{ route('helpdesk.tickets.agent.update', $ticket) }}"
|
||||
class="mb-5 space-y-3">
|
||||
@csrf
|
||||
<label class="block text-sm font-bold text-zinc-700 dark:text-zinc-200" for="show_agent_id">Assigned agent</label>
|
||||
<select id="show_agent_id" name="agent_id" class="hd-select block w-full rounded-xl border border-zinc-300 bg-white p-2.5 text-sm font-semibold text-zinc-900 shadow-sm focus:border-emerald-500 focus:ring-emerald-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100" data-dusk="ticket-show-agent">
|
||||
<label class="block text-sm font-bold text-zinc-700 dark:text-zinc-200"
|
||||
for="show_agent_id">Assigned agent</label>
|
||||
<select id="show_agent_id" name="agent_id"
|
||||
class="hd-select block w-full rounded-xl border border-zinc-300 bg-white p-2.5 text-sm font-semibold text-zinc-900 shadow-sm focus:border-emerald-500 focus:ring-emerald-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100"
|
||||
data-dusk="ticket-show-agent">
|
||||
<option value="" @selected($agent_id === null)>Unassigned</option>
|
||||
@forelse ($agents as $agent)
|
||||
<option value="{{ $agent->id }}" @selected((int) $agent_id === (int) $agent->id)>{{ $agent->name }} · {{ $agent->email }}</option>
|
||||
<option value="{{ $agent->id }}" @selected((int) $agent_id === (int) $agent->id)>{{ $agent->name }} ·
|
||||
{{ $agent->email }}</option>
|
||||
@empty
|
||||
<option value="" disabled>No users available for assignment</option>
|
||||
@endforelse
|
||||
</select>
|
||||
@error('agent_id') <p class="text-sm font-bold text-red-600 dark:text-red-400">{{ $message }}</p> @enderror
|
||||
<button type="submit" dusk="update-agent" class="inline-flex items-center justify-center rounded-lg bg-emerald-600 px-3 py-2 text-xs font-black text-white shadow-sm transition hover:bg-emerald-700 focus:outline-none focus:ring-2 focus:ring-emerald-400/70">
|
||||
@error('agent_id')
|
||||
<p class="text-sm font-bold text-red-600 dark:text-red-400">{{ $message }}</p>
|
||||
@enderror
|
||||
<button type="submit" dusk="update-agent"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-emerald-600 px-3 py-2 text-xs font-black text-white shadow-sm transition hover:bg-emerald-700 focus:outline-none focus:ring-2 focus:ring-emerald-400/70">
|
||||
<i class="fa-solid fa-user-check mr-2" aria-hidden="true"></i>
|
||||
<span>Assign agent</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form method="POST" action="{{ route('helpdesk.tickets.status.update', $ticket) }}" class="mb-5 space-y-3">
|
||||
<form method="POST" action="{{ route('helpdesk.tickets.status.update', $ticket) }}"
|
||||
class="mb-5 space-y-3">
|
||||
@csrf
|
||||
<label class="block text-sm font-bold text-zinc-700 dark:text-zinc-200" for="show_status_id">Status</label>
|
||||
<select id="show_status_id" name="status_id" class="hd-select block w-full rounded-xl border border-zinc-300 bg-white p-2.5 text-sm font-semibold text-zinc-900 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100" data-dusk="ticket-show-status">
|
||||
<label class="block text-sm font-bold text-zinc-700 dark:text-zinc-200"
|
||||
for="show_status_id">Status</label>
|
||||
<select id="show_status_id" name="status_id"
|
||||
class="hd-select block w-full rounded-xl border border-zinc-300 bg-white p-2.5 text-sm font-semibold text-zinc-900 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100"
|
||||
data-dusk="ticket-show-status">
|
||||
@foreach ($statuses as $status)
|
||||
<option value="{{ $status->id }}" @selected((int) $status_id === (int) $status->id)>{{ $status->name }}</option>
|
||||
<option value="{{ $status->id }}" @selected((int) $status_id === (int) $status->id)>{{ $status->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('status_id') <p class="text-sm font-bold text-red-600 dark:text-red-400">{{ $message }}</p> @enderror
|
||||
<button type="submit" dusk="update-status" class="inline-flex items-center justify-center rounded-lg bg-blue-600 px-3 py-2 text-xs font-black text-white shadow-sm transition hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400/70">
|
||||
@error('status_id')
|
||||
<p class="text-sm font-bold text-red-600 dark:text-red-400">{{ $message }}</p>
|
||||
@enderror
|
||||
<button type="submit" dusk="update-status"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-blue-600 px-3 py-2 text-xs font-black text-white shadow-sm transition hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400/70">
|
||||
<i class="fa-solid fa-circle-check mr-2" aria-hidden="true"></i>
|
||||
<span>Update status</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form method="POST" action="{{ route('helpdesk.tickets.priority.update', $ticket) }}" class="space-y-3">
|
||||
<form method="POST" action="{{ route('helpdesk.tickets.priority.update', $ticket) }}"
|
||||
class="space-y-3">
|
||||
@csrf
|
||||
<label class="block text-sm font-bold text-zinc-700 dark:text-zinc-200" for="show_priority_id">Priority</label>
|
||||
<select id="show_priority_id" name="priority_id" class="hd-select block w-full rounded-xl border border-zinc-300 bg-white p-2.5 text-sm font-semibold text-zinc-900 shadow-sm focus:border-amber-500 focus:ring-amber-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100" data-dusk="ticket-show-priority">
|
||||
<label class="block text-sm font-bold text-zinc-700 dark:text-zinc-200"
|
||||
for="show_priority_id">Priority</label>
|
||||
<select id="show_priority_id" name="priority_id"
|
||||
class="hd-select block w-full rounded-xl border border-zinc-300 bg-white p-2.5 text-sm font-semibold text-zinc-900 shadow-sm focus:border-amber-500 focus:ring-amber-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100"
|
||||
data-dusk="ticket-show-priority">
|
||||
@foreach ($priorities as $priority)
|
||||
<option value="{{ $priority->id }}" @selected((int) $priority_id === (int) $priority->id)>{{ $priority->name }}</option>
|
||||
<option value="{{ $priority->id }}" @selected((int) $priority_id === (int) $priority->id)>{{ $priority->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('priority_id') <p class="text-sm font-bold text-red-600 dark:text-red-400">{{ $message }}</p> @enderror
|
||||
<button type="submit" dusk="update-priority" class="inline-flex items-center justify-center rounded-lg bg-amber-500 px-3 py-2 text-xs font-black text-white shadow-sm transition hover:bg-amber-600 focus:outline-none focus:ring-2 focus:ring-amber-300/70">
|
||||
@error('priority_id')
|
||||
<p class="text-sm font-bold text-red-600 dark:text-red-400">{{ $message }}</p>
|
||||
@enderror
|
||||
<button type="submit" dusk="update-priority"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-amber-500 px-3 py-2 text-xs font-black text-white shadow-sm transition hover:bg-amber-600 focus:outline-none focus:ring-2 focus:ring-amber-300/70">
|
||||
<i class="fa-solid fa-flag mr-2" aria-hidden="true"></i>
|
||||
<span>Update priority</span>
|
||||
</button>
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
<x-layouts::app :title="__('Security')">
|
||||
<div class="container-fluid py-4 module-page module-security">
|
||||
<div class="container-fluid py-4 module-page module-security">
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="Security"
|
||||
<x-wms.page-hero eyebrow="Operations" title="Security"
|
||||
description="Control roles, permissions, audit visibility, and protected administration actions."
|
||||
icon="fa-solid fa-shield-halved"
|
||||
/>
|
||||
icon="fa-solid fa-shield-halved" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Roles</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for roles.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Roles</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for roles.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Permissions</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for permissions.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Permissions</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for permissions.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Admin Access</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for admin access.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Admin Access</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for admin access.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Security Audit</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for security audit.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Security Audit</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for security audit.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<section class="card border-0 shadow-sm">
|
||||
@@ -53,7 +50,8 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Security workbench</h2>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.</p>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers,
|
||||
queries, DataTables, or Livewire.</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
@@ -78,26 +76,38 @@
|
||||
<tr>
|
||||
<td>Roles</td>
|
||||
<td>Sample 1</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Permissions</td>
|
||||
<td>Sample 2</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Admin Access</td>
|
||||
<td>Sample 3</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Security Audit</td>
|
||||
<td>Sample 4</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
<x-layouts::app :title="__('Sentinel Integration')">
|
||||
<div class="container-fluid py-4 module-page module-sentinel-integration">
|
||||
<div class="container-fluid py-4 module-page module-sentinel-integration">
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="Sentinel Integration"
|
||||
<x-wms.page-hero eyebrow="Operations" title="Sentinel Integration"
|
||||
description="Track Sentinel API activity, inbound receiver events, and interface status."
|
||||
icon="fa-solid fa-satellite-dish"
|
||||
/>
|
||||
icon="fa-solid fa-satellite-dish" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Sentinel Receiver</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for sentinel receiver.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Sentinel Receiver</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for sentinel receiver.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">API Activity</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for api activity.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">API Activity</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for api activity.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Interface Health</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for interface health.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Interface Health</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for interface health.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Payload Archive</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for payload archive.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Payload Archive</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for payload archive.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<section class="card border-0 shadow-sm">
|
||||
@@ -53,7 +50,8 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Sentinel Integration workbench</h2>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.</p>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers,
|
||||
queries, DataTables, or Livewire.</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
@@ -78,26 +76,38 @@
|
||||
<tr>
|
||||
<td>Sentinel Receiver</td>
|
||||
<td>Sample 1</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>API Activity</td>
|
||||
<td>Sample 2</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Interface Health</td>
|
||||
<td>Sample 3</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Payload Archive</td>
|
||||
<td>Sample 4</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
<x-layouts::app :title="__('Shipping')">
|
||||
<div class="container-fluid py-4 module-page module-shipping">
|
||||
<div class="container-fluid py-4 module-page module-shipping">
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="Shipping"
|
||||
<x-wms.page-hero eyebrow="Operations" title="Shipping"
|
||||
description="Monitor shipment activity, closed orders, pending loads, and dispatch status."
|
||||
icon="fa-solid fa-truck-fast"
|
||||
/>
|
||||
icon="fa-solid fa-truck-fast" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Open Shipments</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for open shipments.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Open Shipments</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for open shipments.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Closed Orders</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for closed orders.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Closed Orders</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for closed orders.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Shipment Close Calls</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for shipment close calls.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Shipment Close Calls</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for shipment close calls.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Dispatch Exceptions</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for dispatch exceptions.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Dispatch Exceptions</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for dispatch exceptions.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<section class="card border-0 shadow-sm">
|
||||
@@ -53,7 +50,8 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Shipping workbench</h2>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.</p>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers,
|
||||
queries, DataTables, or Livewire.</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
@@ -78,26 +76,38 @@
|
||||
<tr>
|
||||
<td>Open Shipments</td>
|
||||
<td>Sample 1</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Closed Orders</td>
|
||||
<td>Sample 2</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Shipment Close Calls</td>
|
||||
<td>Sample 3</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Dispatch Exceptions</td>
|
||||
<td>Sample 4</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
<x-layouts::app :title="__('Transportation')">
|
||||
<div class="container-fluid py-4 module-page module-transportation">
|
||||
<div class="container-fluid py-4 module-page module-transportation">
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="Transportation"
|
||||
<x-wms.page-hero eyebrow="Operations" title="Transportation"
|
||||
description="Manage truck closure, freight terms, trailers, and transport operations."
|
||||
icon="fa-solid fa-route"
|
||||
/>
|
||||
icon="fa-solid fa-route" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Pending Truck Closure</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for pending truck closure.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">01</div>
|
||||
<h3 class="h6 mb-2">Pending Truck Closure</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for pending truck closure.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Trailer Activity</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for trailer activity.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">02</div>
|
||||
<h3 class="h6 mb-2">Trailer Activity</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for trailer activity.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Freight Terms</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for freight terms.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">03</div>
|
||||
<h3 class="h6 mb-2">Freight Terms</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for freight terms.</p>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Transport Exceptions</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for transport exceptions.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card h-100 border-0 shadow-sm module-metric-card">
|
||||
<div class="card-body">
|
||||
<div class="small text-uppercase text-muted fw-semibold mb-2">04</div>
|
||||
<h3 class="h6 mb-2">Transport Exceptions</h3>
|
||||
<p class="small text-muted mb-0">Placeholder area for transport exceptions.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<section class="card border-0 shadow-sm">
|
||||
@@ -53,7 +50,8 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Transportation workbench</h2>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.</p>
|
||||
<p class="small text-muted mb-0">Sample table layout ready to wire into controllers,
|
||||
queries, DataTables, or Livewire.</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
@@ -78,26 +76,38 @@
|
||||
<tr>
|
||||
<td>Pending Truck Closure</td>
|
||||
<td>Sample 1</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Trailer Activity</td>
|
||||
<td>Sample 2</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Freight Terms</td>
|
||||
<td>Sample 3</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Transport Exceptions</td>
|
||||
<td>Sample 4</td>
|
||||
<td><span class="badge bg-success-subtle text-success border border-success-subtle">Active</span></td>
|
||||
<td class="text-end"><a href="#" class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
<td><span
|
||||
class="badge bg-success-subtle text-success border border-success-subtle">Active</span>
|
||||
</td>
|
||||
<td class="text-end"><a href="#"
|
||||
class="btn btn-sm btn-outline-primary">Open</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,24 +1,57 @@
|
||||
<x-layouts::app :title="__('Validation Rules')">
|
||||
<div class="space-y-6">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Administration"
|
||||
title="Validation Rules"
|
||||
<x-wms.page-hero eyebrow="Administration" title="Validation Rules"
|
||||
description="Configure warehouse validation controls, severity levels and operational rule keys."
|
||||
icon="fa-solid fa-shield-halved"
|
||||
/>
|
||||
icon="fa-solid fa-shield-halved" />
|
||||
<div class="rounded-3xl bg-white p-6 shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800">
|
||||
<h2 class="text-lg font-bold text-zinc-900 dark:text-white">Add validation rule</h2>
|
||||
<form method="POST" action="{{ route('warehouse-rules.store') }}" class="mt-5 grid gap-4 md:grid-cols-6">
|
||||
@csrf
|
||||
<select name="warehouse_id" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">@foreach($warehouses as $warehouse)<option value="{{ $warehouse->id }}">{{ $warehouse->name }}</option>@endforeach</select>
|
||||
<input name="name" placeholder="Rule name" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<input name="rule_key" placeholder="stock.location.required" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<select name="severity" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950"><option>error</option><option>warning</option><option>blocker</option><option>info</option></select>
|
||||
<input name="configuration" placeholder='{"min": 1}' class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<button class="rounded-xl bg-zinc-900 px-4 py-2 font-semibold text-white dark:bg-white dark:text-zinc-900">Add rule</button>
|
||||
<select name="warehouse_id" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
@foreach ($warehouses as $warehouse)
|
||||
<option value="{{ $warehouse->id }}">{{ $warehouse->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<input name="name" placeholder="Rule name"
|
||||
class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<input name="rule_key" placeholder="stock.location.required"
|
||||
class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<select name="severity" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<option>error</option>
|
||||
<option>warning</option>
|
||||
<option>blocker</option>
|
||||
<option>info</option>
|
||||
</select>
|
||||
<input name="configuration" placeholder='{"min": 1}'
|
||||
class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<button
|
||||
class="rounded-xl bg-zinc-900 px-4 py-2 font-semibold text-white dark:bg-white dark:text-zinc-900">Add
|
||||
rule</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="overflow-x-auto rounded-2xl bg-white shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800"><table class="min-w-full text-sm"><thead><tr class="border-b"><th class="p-4 text-left">Warehouse</th><th class="p-4 text-left">Rule</th><th class="p-4 text-left">Key</th><th class="p-4 text-left">Severity</th></tr></thead><tbody>@foreach($rules as $rule)<tr class="border-b border-zinc-100 dark:border-zinc-800"><td class="p-4">{{ $rule->warehouse->name }}</td><td class="p-4 font-semibold">{{ $rule->name }}</td><td class="p-4"><code>{{ $rule->rule_key }}</code></td><td class="p-4">{{ $rule->severity }}</td></tr>@endforeach</tbody></table></div>
|
||||
<div
|
||||
class="overflow-x-auto rounded-2xl bg-white shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b">
|
||||
<th class="p-4 text-left">Warehouse</th>
|
||||
<th class="p-4 text-left">Rule</th>
|
||||
<th class="p-4 text-left">Key</th>
|
||||
<th class="p-4 text-left">Severity</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($rules as $rule)
|
||||
<tr class="border-b border-zinc-100 dark:border-zinc-800">
|
||||
<td class="p-4">{{ $rule->warehouse->name }}</td>
|
||||
<td class="p-4 font-semibold">{{ $rule->name }}</td>
|
||||
<td class="p-4"><code>{{ $rule->rule_key }}</code></td>
|
||||
<td class="p-4">{{ $rule->severity }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ $rules->links() }}
|
||||
</div>
|
||||
</x-layouts::app>
|
||||
|
||||
@@ -1,23 +1,35 @@
|
||||
<x-layouts::app :title="__('WMS Dashboard')">
|
||||
<div class="space-y-6">
|
||||
<x-wms.page-hero
|
||||
eyebrow="MPBA WMS"
|
||||
:title="auth()->user()->company->name"
|
||||
<x-wms.page-hero eyebrow="MPBA WMS" :title="auth()->user()->company->name"
|
||||
description="Tenant warehouse dashboard for operational status, warehouse coverage and validation readiness."
|
||||
:stats="[
|
||||
['label' => 'Warehouses', 'value' => $warehouses->count(), 'icon' => 'fa-solid fa-warehouse'],
|
||||
['label' => 'Status', 'value' => 'Live', 'icon' => 'fa-solid fa-circle-check'],
|
||||
]"
|
||||
/>
|
||||
]" />
|
||||
|
||||
<section class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
@forelse($warehouses as $warehouse)
|
||||
<article class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<div class="flex items-start justify-between gap-4"><div><p class="text-sm font-semibold text-cyan-600">{{ $warehouse->code }}</p><h2 class="mt-1 text-xl font-bold">{{ $warehouse->name }}</h2></div><span class="rounded-full bg-emerald-100 px-3 py-1 text-xs font-semibold text-emerald-800">{{ $warehouse->status }}</span></div>
|
||||
<p class="mt-4 text-sm text-zinc-500">{{ $warehouse->validation_rules_count }} validation rules configured.</p>
|
||||
<article
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<p class="text-sm font-semibold text-cyan-600">{{ $warehouse->code }}</p>
|
||||
<h2 class="mt-1 text-xl font-bold">{{ $warehouse->name }}</h2>
|
||||
</div><span
|
||||
class="rounded-full bg-emerald-100 px-3 py-1 text-xs font-semibold text-emerald-800">{{ $warehouse->status }}</span>
|
||||
</div>
|
||||
<p class="mt-4 text-sm text-zinc-500">{{ $warehouse->validation_rules_count }} validation rules
|
||||
configured.</p>
|
||||
</article>
|
||||
@empty
|
||||
<article class="rounded-2xl border border-dashed border-zinc-300 bg-white p-8 text-center dark:border-zinc-800 dark:bg-zinc-900 md:col-span-2 xl:col-span-3"><h2 class="text-xl font-bold">Create your first warehouse</h2><p class="mt-2 text-zinc-500">Warehouses are isolated to your company tenant.</p><a href="{{ route('warehouses.index') }}" class="mt-5 inline-flex rounded-xl bg-zinc-900 px-4 py-2 font-semibold text-white dark:bg-white dark:text-zinc-900">Add warehouse</a></article>
|
||||
<article
|
||||
class="rounded-2xl border border-dashed border-zinc-300 bg-white p-8 text-center dark:border-zinc-800 dark:bg-zinc-900 md:col-span-2 xl:col-span-3">
|
||||
<h2 class="text-xl font-bold">Create your first warehouse</h2>
|
||||
<p class="mt-2 text-zinc-500">Warehouses are isolated to your company tenant.</p><a
|
||||
href="{{ route('warehouses.index') }}"
|
||||
class="mt-5 inline-flex rounded-xl bg-zinc-900 px-4 py-2 font-semibold text-white dark:bg-white dark:text-zinc-900">Add
|
||||
warehouse</a>
|
||||
</article>
|
||||
@endforelse
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
<x-layouts::app :title="__('Warehouses')">
|
||||
<div class="space-y-6">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Administration"
|
||||
title="Warehouses"
|
||||
<x-wms.page-hero eyebrow="Administration" title="Warehouses"
|
||||
description="Create and manage warehouse records, tenant ownership and operational locations."
|
||||
icon="fa-solid fa-warehouse"
|
||||
/>
|
||||
icon="fa-solid fa-warehouse" />
|
||||
<div class="rounded-3xl bg-white p-6 shadow-sm ring-1 ring-zinc-200 dark:bg-zinc-900 dark:ring-zinc-800">
|
||||
<h2 class="text-lg font-bold text-zinc-900 dark:text-white">Create warehouse</h2>
|
||||
<form method="POST" action="{{ route('warehouses.store') }}" class="mt-5 grid gap-4 md:grid-cols-4">
|
||||
@csrf
|
||||
<input name="code" placeholder="Code" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<input name="name" placeholder="Warehouse name" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<input name="address" placeholder="Address" class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<button class="rounded-xl bg-zinc-900 px-4 py-2 font-semibold text-white dark:bg-white dark:text-zinc-900">Create</button>
|
||||
<input name="code" placeholder="Code"
|
||||
class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<input name="name" placeholder="Warehouse name"
|
||||
class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950" required>
|
||||
<input name="address" placeholder="Address"
|
||||
class="rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<button
|
||||
class="rounded-xl bg-zinc-900 px-4 py-2 font-semibold text-white dark:bg-white dark:text-zinc-900">Create</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
@foreach($warehouses as $warehouse)
|
||||
<article class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-800 dark:bg-zinc-900"><p class="text-sm text-cyan-600">{{ $warehouse->code }}</p><h2 class="mt-1 text-xl font-bold">{{ $warehouse->name }}</h2><p class="mt-3 text-sm text-zinc-500">{{ $warehouse->validation_rules_count }} rules</p></article>
|
||||
@foreach ($warehouses as $warehouse)
|
||||
<article
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<p class="text-sm text-cyan-600">{{ $warehouse->code }}</p>
|
||||
<h2 class="mt-1 text-xl font-bold">{{ $warehouse->name }}</h2>
|
||||
<p class="mt-3 text-sm text-zinc-500">{{ $warehouse->validation_rules_count }} rules</p>
|
||||
</article>
|
||||
@endforeach
|
||||
</div>
|
||||
{{ $warehouses->links() }}
|
||||
|
||||
+3
-1
@@ -8,7 +8,8 @@
|
||||
"modules:build": "node scripts/build-modules.mjs",
|
||||
"build:all": "npm run build && npm run modules:build",
|
||||
"modules:install": "find Modules -maxdepth 2 -name package.json -not -path '*/node_modules/*' -execdir npm install \\;",
|
||||
"install:all": "npm install && npm run modules:install"
|
||||
"install:all": "npm install && npm run modules:install",
|
||||
"format:blade": "blade-formatter \"resources/views/**/*.blade.php\" \"Modules/**/*.blade.php\" --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^7.2.0",
|
||||
@@ -32,6 +33,7 @@
|
||||
"lightningcss-linux-x64-gnu": "^1.29.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"blade-formatter": "^1.44.4",
|
||||
"chokidar": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 42" {{ $attributes }}>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M17.2 5.633 8.6.855 0 5.633v26.51l16.2 9 16.2-9v-8.442l7.6-4.223V9.856l-8.6-4.777-8.6 4.777V18.3l-5.6 3.111V5.633ZM38 18.301l-5.6 3.11v-6.157l5.6-3.11V18.3Zm-1.06-7.856-5.54 3.078-5.54-3.079 5.54-3.078 5.54 3.079ZM24.8 18.3v-6.157l5.6 3.111v6.158L24.8 18.3Zm-1 1.732 5.54 3.078-13.14 7.302-5.54-3.078 13.14-7.3v-.002Zm-16.2 7.89 7.6 4.222V38.3L2 30.966V7.92l5.6 3.111v16.892ZM8.6 9.3 3.06 6.222 8.6 3.143l5.54 3.08L8.6 9.3Zm21.8 15.51-13.2 7.334V38.3l13.2-7.334v-6.156ZM9.6 11.034l5.6-3.11v14.6l-5.6 3.11v-14.6Z"
|
||||
/>
|
||||
<path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M17.2 5.633 8.6.855 0 5.633v26.51l16.2 9 16.2-9v-8.442l7.6-4.223V9.856l-8.6-4.777-8.6 4.777V18.3l-5.6 3.111V5.633ZM38 18.301l-5.6 3.11v-6.157l5.6-3.11V18.3Zm-1.06-7.856-5.54 3.078-5.54-3.079 5.54-3.078 5.54 3.079ZM24.8 18.3v-6.157l5.6 3.111v6.158L24.8 18.3Zm-1 1.732 5.54 3.078-13.14 7.302-5.54-3.078 13.14-7.3v-.002Zm-16.2 7.89 7.6 4.222V38.3L2 30.966V7.92l5.6 3.111v16.892ZM8.6 9.3 3.06 6.222 8.6 3.143l5.54 3.08L8.6 9.3Zm21.8 15.51-13.2 7.334V38.3l13.2-7.334v-6.156ZM9.6 11.034l5.6-3.11v14.6l-5.6 3.11v-14.6Z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 714 B After Width: | Height: | Size: 683 B |
@@ -2,15 +2,17 @@
|
||||
'sidebar' => false,
|
||||
])
|
||||
|
||||
@if($sidebar)
|
||||
@if ($sidebar)
|
||||
<flux:sidebar.brand name="Warehouse Management System" {{ $attributes }}>
|
||||
<x-slot name="logo" class="flex aspect-square size-8 items-center justify-center rounded-md bg-accent-content text-accent-foreground">
|
||||
<x-slot name="logo"
|
||||
class="flex aspect-square size-8 items-center justify-center rounded-md bg-accent-content text-accent-foreground">
|
||||
<x-app-logo-icon class="size-5 fill-current text-white dark:text-black" />
|
||||
</x-slot>
|
||||
</flux:sidebar.brand>
|
||||
@else
|
||||
<flux:brand name="Warehouse Management System" {{ $attributes }}>
|
||||
<x-slot name="logo" class="flex aspect-square size-8 items-center justify-center rounded-md bg-accent-content text-accent-foreground">
|
||||
<x-slot name="logo"
|
||||
class="flex aspect-square size-8 items-center justify-center rounded-md bg-accent-content text-accent-foreground">
|
||||
<x-app-logo-icon class="size-5 fill-current text-white dark:text-black" />
|
||||
</x-slot>
|
||||
</flux:brand>
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
@props([
|
||||
'title',
|
||||
'description',
|
||||
])
|
||||
@props(['title', 'description'])
|
||||
|
||||
<div class="flex w-full flex-col text-center">
|
||||
<flux:heading size="xl">{{ $title }}</flux:heading>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
@props([
|
||||
'status',
|
||||
])
|
||||
@props(['status'])
|
||||
|
||||
@if ($status)
|
||||
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600']) }}>
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
@props([
|
||||
'title',
|
||||
'description' => '',
|
||||
'type' => 'line',
|
||||
'config' => [],
|
||||
'height' => 300,
|
||||
'icon' => 'fa-chart-line',
|
||||
])
|
||||
@props(['title', 'description' => '', 'type' => 'line', 'config' => [], 'height' => 300, 'icon' => 'fa-chart-line'])
|
||||
|
||||
<div class="card wms-chart-card mb-4 h-100">
|
||||
<div class="card-header wms-chart-card__header">
|
||||
@@ -14,7 +7,7 @@
|
||||
<h3 class="wms-chart-card__title">
|
||||
<i class="fas {{ $icon }} me-2"></i>{{ $title }}
|
||||
</h3>
|
||||
@if($description !== '')
|
||||
@if ($description !== '')
|
||||
<p class="wms-chart-card__description">{{ $description }}</p>
|
||||
@endif
|
||||
</div>
|
||||
@@ -22,13 +15,8 @@
|
||||
|
||||
<div class="card-body">
|
||||
<div class="wms-chart-card__canvas" style="height: {{ (int) $height }}px;">
|
||||
<canvas
|
||||
data-wms-chart
|
||||
data-chart-type="{{ $type }}"
|
||||
data-chart-config='@json($config)'
|
||||
aria-label="{{ $title }} chart"
|
||||
role="img"
|
||||
></canvas>
|
||||
<canvas data-wms-chart data-chart-type="{{ $type }}" data-chart-config='@json($config)'
|
||||
aria-label="{{ $title }} chart" role="img"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
@props([
|
||||
'label',
|
||||
'value' => 0,
|
||||
'hint' => '',
|
||||
'icon' => 'fa-chart-simple',
|
||||
])
|
||||
@props(['label', 'value' => 0, 'hint' => '', 'icon' => 'fa-chart-simple'])
|
||||
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card wms-metric-card mb-4">
|
||||
@@ -11,7 +6,7 @@
|
||||
<div>
|
||||
<div class="wms-metric-card__label">{{ $label }}</div>
|
||||
<div class="wms-metric-card__value">{{ is_numeric($value) ? number_format((int) $value) : $value }}</div>
|
||||
@if($hint !== '')
|
||||
@if ($hint !== '')
|
||||
<div class="wms-metric-card__hint">{{ $hint }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
<flux:dropdown position="bottom" align="start">
|
||||
<flux:sidebar.profile
|
||||
:name="auth()->user()->name"
|
||||
:initials="auth()->user()->initials()"
|
||||
icon:trailing="chevrons-up-down"
|
||||
data-test="sidebar-menu-button"
|
||||
/>
|
||||
<flux:sidebar.profile :name="auth()->user()->name" :initials="auth()->user()->initials()"
|
||||
icon:trailing="chevrons-up-down" data-test="sidebar-menu-button" />
|
||||
|
||||
<flux:menu>
|
||||
<div class="flex items-center gap-2 px-1 py-1.5 text-start text-sm">
|
||||
<flux:avatar
|
||||
:name="auth()->user()->name"
|
||||
:initials="auth()->user()->initials()"
|
||||
/>
|
||||
<flux:avatar :name="auth()->user()->name" :initials="auth()->user()->initials()" />
|
||||
<div class="grid flex-1 text-start text-sm leading-tight">
|
||||
<flux:heading class="truncate">{{ auth()->user()->name }}</flux:heading>
|
||||
<flux:text class="truncate">{{ auth()->user()->email }}</flux:text>
|
||||
@@ -24,13 +17,8 @@
|
||||
</flux:menu.item>
|
||||
<form method="POST" action="{{ route('logout') }}" class="w-full">
|
||||
@csrf
|
||||
<flux:menu.item
|
||||
as="button"
|
||||
type="submit"
|
||||
icon="arrow-right-start-on-rectangle"
|
||||
class="w-full cursor-pointer"
|
||||
data-test="logout-button"
|
||||
>
|
||||
<flux:menu.item as="button" type="submit" icon="arrow-right-start-on-rectangle"
|
||||
class="w-full cursor-pointer" data-test="logout-button">
|
||||
{{ __('Log out') }}
|
||||
</flux:menu.item>
|
||||
</form>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,59 +1,53 @@
|
||||
@assets
|
||||
@vite('resources/js/passkeys.js')
|
||||
@vite('resources/js/passkeys.js')
|
||||
@endassets
|
||||
|
||||
<div
|
||||
x-data="{
|
||||
supported: false,
|
||||
showForm: false,
|
||||
name: '',
|
||||
loading: false,
|
||||
error: null,
|
||||
updateSupport() {
|
||||
this.supported = Boolean(window.Passkeys?.isSupported());
|
||||
},
|
||||
init() {
|
||||
this.updateSupport();
|
||||
<div x-data="{
|
||||
supported: false,
|
||||
showForm: false,
|
||||
name: '',
|
||||
loading: false,
|
||||
error: null,
|
||||
updateSupport() {
|
||||
this.supported = Boolean(window.Passkeys?.isSupported());
|
||||
},
|
||||
init() {
|
||||
this.updateSupport();
|
||||
|
||||
window.addEventListener('passkeys:ready', () => this.updateSupport(), { once: true });
|
||||
},
|
||||
async register() {
|
||||
if (!this.name.trim()) return;
|
||||
window.addEventListener('passkeys:ready', () => this.updateSupport(), { once: true });
|
||||
},
|
||||
async register() {
|
||||
if (!this.name.trim()) return;
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
await window.Passkeys.register({ name: this.name });
|
||||
this.name = '';
|
||||
this.showForm = false;
|
||||
await $wire.loadPasskeys();
|
||||
} catch (e) {
|
||||
if (e.constructor?.name !== 'UserCancelledError') {
|
||||
this.error = e.message;
|
||||
}
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
cancel() {
|
||||
this.showForm = false;
|
||||
try {
|
||||
await window.Passkeys.register({ name: this.name });
|
||||
this.name = '';
|
||||
this.error = null;
|
||||
},
|
||||
}"
|
||||
>
|
||||
this.showForm = false;
|
||||
await $wire.loadPasskeys();
|
||||
} catch (e) {
|
||||
if (e.constructor?.name !== 'UserCancelledError') {
|
||||
this.error = e.message;
|
||||
}
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
cancel() {
|
||||
this.showForm = false;
|
||||
this.name = '';
|
||||
this.error = null;
|
||||
},
|
||||
}">
|
||||
<template x-if="!supported">
|
||||
<flux:text>{{ __('Passkeys are not supported in this browser.') }}</flux:text>
|
||||
</template>
|
||||
|
||||
<template x-if="supported && !showForm">
|
||||
<div>
|
||||
<flux:button
|
||||
variant="primary"
|
||||
icon="plus"
|
||||
x-on:click="showForm = true"
|
||||
>
|
||||
<flux:button variant="primary" icon="plus" x-on:click="showForm = true">
|
||||
{{ __('Add passkey') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
@@ -61,31 +55,19 @@
|
||||
|
||||
<template x-if="supported && showForm">
|
||||
<div class="space-y-4 rounded-lg border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800/50 p-4">
|
||||
<flux:input
|
||||
label="{{ __('Passkey name') }}"
|
||||
x-model="name"
|
||||
placeholder="{{ __('e.g., MacBook Pro, iPhone') }}"
|
||||
x-on:keydown.enter.prevent="register()"
|
||||
x-ref="passkeyNameInput"
|
||||
x-init="$nextTick(() => $refs.passkeyNameInput?.focus())"
|
||||
/>
|
||||
<flux:input label="{{ __('Passkey name') }}" x-model="name"
|
||||
placeholder="{{ __('e.g., MacBook Pro, iPhone') }}" x-on:keydown.enter.prevent="register()"
|
||||
x-ref="passkeyNameInput" x-init="$nextTick(() => $refs.passkeyNameInput?.focus())" />
|
||||
<flux:text class="!mt-1">{{ __('Give this passkey a name to help you identify it later.') }}</flux:text>
|
||||
|
||||
<p x-show="error" x-text="error" x-cloak class="text-sm text-red-600 dark:text-red-400"></p>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<flux:button
|
||||
variant="primary"
|
||||
x-on:click="register()"
|
||||
x-bind:disabled="loading || !name.trim()"
|
||||
>
|
||||
<flux:button variant="primary" x-on:click="register()" x-bind:disabled="loading || !name.trim()">
|
||||
<span x-show="!loading">{{ __('Register passkey') }}</span>
|
||||
<span x-show="loading" x-cloak>{{ __('Registering...') }}</span>
|
||||
</flux:button>
|
||||
<flux:button
|
||||
variant="ghost"
|
||||
x-on:click="cancel()"
|
||||
>
|
||||
<flux:button variant="ghost" x-on:click="cancel()">
|
||||
{{ __('Cancel') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
|
||||
@@ -7,58 +7,50 @@
|
||||
])
|
||||
|
||||
@assets
|
||||
@vite('resources/js/passkeys.js')
|
||||
@vite('resources/js/passkeys.js')
|
||||
@endassets
|
||||
|
||||
<div
|
||||
x-data="{
|
||||
supported: false,
|
||||
loading: false,
|
||||
error: null,
|
||||
updateSupport() {
|
||||
this.supported = Boolean(window.Passkeys?.isSupported());
|
||||
},
|
||||
init() {
|
||||
this.updateSupport();
|
||||
<div x-data="{
|
||||
supported: false,
|
||||
loading: false,
|
||||
error: null,
|
||||
updateSupport() {
|
||||
this.supported = Boolean(window.Passkeys?.isSupported());
|
||||
},
|
||||
init() {
|
||||
this.updateSupport();
|
||||
|
||||
window.addEventListener('passkeys:ready', () => this.updateSupport(), { once: true });
|
||||
},
|
||||
async verify() {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
try {
|
||||
const response = await window.Passkeys.verify({
|
||||
routes: {
|
||||
options: '{{ route($optionsRoute) }}',
|
||||
submit: '{{ route($submitRoute) }}',
|
||||
},
|
||||
});
|
||||
Livewire.navigate(response.redirect || '/dashboard');
|
||||
} catch (e) {
|
||||
if (e.constructor?.name !== 'UserCancelledError') {
|
||||
this.error = e.message;
|
||||
}
|
||||
} finally {
|
||||
this.loading = false;
|
||||
window.addEventListener('passkeys:ready', () => this.updateSupport(), { once: true });
|
||||
},
|
||||
async verify() {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
try {
|
||||
const response = await window.Passkeys.verify({
|
||||
routes: {
|
||||
options: '{{ route($optionsRoute) }}',
|
||||
submit: '{{ route($submitRoute) }}',
|
||||
},
|
||||
});
|
||||
Livewire.navigate(response.redirect || '/dashboard');
|
||||
} catch (e) {
|
||||
if (e.constructor?.name !== 'UserCancelledError') {
|
||||
this.error = e.message;
|
||||
}
|
||||
},
|
||||
}"
|
||||
>
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
}">
|
||||
<template x-if="supported">
|
||||
<div>
|
||||
<div class="grid gap-2">
|
||||
<flux:button
|
||||
variant="outline"
|
||||
icon="finger-print"
|
||||
class="w-full"
|
||||
x-on:click="verify()"
|
||||
x-bind:disabled="loading"
|
||||
>
|
||||
<flux:button variant="outline" icon="finger-print" class="w-full" x-on:click="verify()"
|
||||
x-bind:disabled="loading">
|
||||
<span x-show="!loading">{{ $label }}</span>
|
||||
<span x-show="loading" x-cloak>{{ $loadingLabel }}</span>
|
||||
</flux:button>
|
||||
<p x-show="error" x-text="error" x-cloak
|
||||
class="text-sm text-center text-red-600 dark:text-red-400"></p>
|
||||
<p x-show="error" x-text="error" x-cloak class="text-sm text-center text-red-600 dark:text-red-400"></p>
|
||||
</div>
|
||||
|
||||
<div class="relative my-6">
|
||||
|
||||
@@ -10,17 +10,17 @@
|
||||
<div class="wms-page-hero-copy">
|
||||
<p class="wms-page-hero-kicker">{{ $eyebrow }}</p>
|
||||
<h1 class="wms-page-hero-title">{{ $title }}</h1>
|
||||
@if($description)
|
||||
@if ($description)
|
||||
<p class="wms-page-hero-description">{{ $description }}</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="wms-page-hero-actions">
|
||||
@foreach($stats as $stat)
|
||||
@foreach ($stats as $stat)
|
||||
<article class="wms-page-hero-stat">
|
||||
<span>{{ $stat['label'] ?? '' }}</span>
|
||||
<strong>
|
||||
@if(! empty($stat['icon']))
|
||||
@if (!empty($stat['icon']))
|
||||
<i class="{{ $stat['icon'] }}" aria-hidden="true"></i>
|
||||
@endif
|
||||
{{ $stat['value'] ?? '' }}
|
||||
@@ -28,13 +28,13 @@
|
||||
</article>
|
||||
@endforeach
|
||||
|
||||
@if($icon && count($stats) === 0)
|
||||
@if ($icon && count($stats) === 0)
|
||||
<article class="wms-page-hero-stat wms-page-hero-icon-card" aria-hidden="true">
|
||||
<strong><i class="{{ $icon }}"></i></strong>
|
||||
</article>
|
||||
@endif
|
||||
|
||||
@if($slot->isNotEmpty())
|
||||
@if ($slot->isNotEmpty())
|
||||
{{ $slot }}
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
<x-layouts::app :title="__('Dashboard')">
|
||||
<x-wms.page-hero
|
||||
eyebrow="Operations"
|
||||
title="Warehouse Command Centre"
|
||||
<x-wms.page-hero eyebrow="Operations" title="Warehouse Command Centre"
|
||||
description="Open inventory, shipping, replenishment, analytics and administration workspaces from one clean operational dashboard."
|
||||
icon="fa-solid fa-gauge-high"
|
||||
/>
|
||||
icon="fa-solid fa-gauge-high" />
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-3">
|
||||
<section class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<p class="text-sm font-semibold uppercase tracking-[0.2em] text-cyan-600 dark:text-cyan-300">Operations</p>
|
||||
<h2 class="mt-3 text-2xl font-bold text-zinc-900 dark:text-white">Warehouse command centre</h2>
|
||||
<p class="mt-2 text-sm leading-6 text-zinc-600 dark:text-zinc-300">Use the navigation to open inventory, shipping, replenishment, analytics and administration workspaces.</p>
|
||||
<p class="mt-2 text-sm leading-6 text-zinc-600 dark:text-zinc-300">Use the navigation to open inventory,
|
||||
shipping, replenishment, analytics and administration workspaces.</p>
|
||||
</section>
|
||||
|
||||
<section class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<p class="text-sm font-semibold uppercase tracking-[0.2em] text-emerald-600 dark:text-emerald-300">Health</p>
|
||||
<p class="text-sm font-semibold uppercase tracking-[0.2em] text-emerald-600 dark:text-emerald-300">Health
|
||||
</p>
|
||||
<h2 class="mt-3 text-2xl font-bold text-zinc-900 dark:text-white">System OK</h2>
|
||||
<p class="mt-2 text-sm leading-6 text-zinc-600 dark:text-zinc-300">Core platform services are ready for module integration, testing and tenant configuration.</p>
|
||||
<p class="mt-2 text-sm leading-6 text-zinc-600 dark:text-zinc-300">Core platform services are ready for
|
||||
module integration, testing and tenant configuration.</p>
|
||||
</section>
|
||||
|
||||
<section class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<section
|
||||
class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<p class="text-sm font-semibold uppercase tracking-[0.2em] text-sky-600 dark:text-sky-300">Modules</p>
|
||||
<h2 class="mt-3 text-2xl font-bold text-zinc-900 dark:text-white">Enabled workspaces</h2>
|
||||
<p class="mt-2 text-sm leading-6 text-zinc-600 dark:text-zinc-300">Placeholder modules are wired into the sidebar and ready for production controllers.</p>
|
||||
<p class="mt-2 text-sm leading-6 text-zinc-600 dark:text-zinc-300">Placeholder modules are wired into the
|
||||
sidebar and ready for production controllers.</p>
|
||||
</section>
|
||||
</div>
|
||||
</x-layouts::app>
|
||||
|
||||
@@ -1,54 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>@yield('title')</title>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!-- Styles -->
|
||||
<style>
|
||||
html, body {
|
||||
background-color: #fff;
|
||||
color: #636b6f;
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-weight: 100;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
<title>@yield('title')</title>
|
||||
|
||||
.full-height {
|
||||
height: 100vh;
|
||||
}
|
||||
<!-- Styles -->
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
background-color: #fff;
|
||||
color: #636b6f;
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-weight: 100;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.full-height {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.position-ref {
|
||||
position: relative;
|
||||
}
|
||||
.flex-center {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
}
|
||||
.position-ref {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36px;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);" aria-hidden="true"></div>
|
||||
<div class="flex-center position-ref full-height">
|
||||
<div class="content">
|
||||
<div class="title">
|
||||
@yield('message')
|
||||
</div>
|
||||
.content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36px;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
|
||||
aria-hidden="true"></div>
|
||||
<div class="flex-center position-ref full-height">
|
||||
<div class="content">
|
||||
<div class="title">
|
||||
@yield('message')
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -25,23 +25,14 @@
|
||||
};
|
||||
@endphp
|
||||
|
||||
<svg
|
||||
{{ $attributes->class($classes) }}
|
||||
data-flux-icon
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="{{ $strokeWidth }}"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
data-slot="icon"
|
||||
>
|
||||
<svg {{ $attributes->class($classes) }} data-flux-icon xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor" stroke-width="{{ $strokeWidth }}" stroke-linecap="round" stroke-linejoin="round"
|
||||
aria-hidden="true" data-slot="icon">
|
||||
<path d="M12 7v14" />
|
||||
<path d="M16 12h2" />
|
||||
<path d="M16 8h2" />
|
||||
<path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z" />
|
||||
<path
|
||||
d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z" />
|
||||
<path d="M6 12h2" />
|
||||
<path d="M6 8h2" />
|
||||
</svg>
|
||||
|
||||
@@ -25,19 +25,9 @@
|
||||
};
|
||||
@endphp
|
||||
|
||||
<svg
|
||||
{{ $attributes->class($classes) }}
|
||||
data-flux-icon
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="{{ $strokeWidth }}"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
data-slot="icon"
|
||||
>
|
||||
<svg {{ $attributes->class($classes) }} data-flux-icon xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor" stroke-width="{{ $strokeWidth }}" stroke-linecap="round" stroke-linejoin="round"
|
||||
aria-hidden="true" data-slot="icon">
|
||||
<path d="m7 15 5 5 5-5" />
|
||||
<path d="m7 9 5-5 5 5" />
|
||||
</svg>
|
||||
|
||||
@@ -25,19 +25,9 @@
|
||||
};
|
||||
@endphp
|
||||
|
||||
<svg
|
||||
{{ $attributes->class($classes) }}
|
||||
data-flux-icon
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="{{ $strokeWidth }}"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
data-slot="icon"
|
||||
>
|
||||
<svg {{ $attributes->class($classes) }} data-flux-icon xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor" stroke-width="{{ $strokeWidth }}" stroke-linecap="round" stroke-linejoin="round"
|
||||
aria-hidden="true" data-slot="icon">
|
||||
<path d="M9 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v5" />
|
||||
<circle cx="13" cy="12" r="2" />
|
||||
<path d="M18 19c-2.8 0-5-2.2-5-5v8" />
|
||||
|
||||
@@ -25,19 +25,9 @@
|
||||
};
|
||||
@endphp
|
||||
|
||||
<svg
|
||||
{{ $attributes->class($classes) }}
|
||||
data-flux-icon
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="{{ $strokeWidth }}"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
data-slot="icon"
|
||||
>
|
||||
<svg {{ $attributes->class($classes) }} data-flux-icon xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor" stroke-width="{{ $strokeWidth }}" stroke-linecap="round" stroke-linejoin="round"
|
||||
aria-hidden="true" data-slot="icon">
|
||||
<rect width="7" height="7" x="3" y="3" rx="1" />
|
||||
<rect width="7" height="7" x="14" y="3" rx="1" />
|
||||
<rect width="7" height="7" x="14" y="14" rx="1" />
|
||||
|
||||
@@ -6,15 +6,10 @@
|
||||
|
||||
<?php if ($expandable && $heading): ?>
|
||||
|
||||
<ui-disclosure
|
||||
{{ $attributes->class('group/disclosure') }}
|
||||
@if ($expanded === true) open @endif
|
||||
data-flux-navlist-group
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="group/disclosure-button mb-[2px] flex h-10 w-full items-center rounded-lg text-zinc-500 hover:bg-zinc-800/5 hover:text-zinc-800 lg:h-8 dark:text-white/80 dark:hover:bg-white/[7%] dark:hover:text-white"
|
||||
>
|
||||
<ui-disclosure {{ $attributes->class('group/disclosure') }} @if ($expanded === true) open @endif
|
||||
data-flux-navlist-group>
|
||||
<button type="button"
|
||||
class="group/disclosure-button mb-[2px] flex h-10 w-full items-center rounded-lg text-zinc-500 hover:bg-zinc-800/5 hover:text-zinc-800 lg:h-8 dark:text-white/80 dark:hover:bg-white/[7%] dark:hover:text-white">
|
||||
<div class="ps-3 pe-4">
|
||||
<flux:icon.chevron-down class="hidden size-3! group-data-open/disclosure-button:block" />
|
||||
<flux:icon.chevron-right class="block size-3! group-data-open/disclosure-button:hidden" />
|
||||
|
||||
@@ -3,10 +3,7 @@
|
||||
@endphp
|
||||
|
||||
@if (request()->routeIs('helpdesk.*') && file_exists($helpdeskManifestPath))
|
||||
@vite([
|
||||
'Resources/assets/js/app.js',
|
||||
'Resources/assets/css/app.css',
|
||||
], 'build/helpdesk')
|
||||
@vite(['Resources/assets/js/app.js', 'Resources/assets/css/app.css'], 'build/helpdesk')
|
||||
@endif
|
||||
|
||||
<x-layouts::app.sidebar :title="$title ?? null">
|
||||
|
||||
@@ -1,85 +1,85 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
|
||||
|
||||
@include('partials.head')
|
||||
@include('partials.head')
|
||||
|
||||
<body class="min-h-screen bg-white dark:bg-zinc-800">
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);" aria-hidden="true"></div>
|
||||
<flux:header container class="border-b border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<flux:sidebar.toggle class="lg:hidden mr-2" icon="bars-2" inset="left" />
|
||||
<body class="min-h-screen bg-white dark:bg-zinc-800">
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
|
||||
aria-hidden="true"></div>
|
||||
<flux:header container class="border-b border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<flux:sidebar.toggle class="lg:hidden mr-2" icon="bars-2" inset="left" />
|
||||
|
||||
<x-app-logo href="{{ route('dashboard') }}" wire:navigate />
|
||||
<x-app-logo href="{{ route('dashboard') }}" wire:navigate />
|
||||
|
||||
<flux:navbar class="-mb-px max-lg:hidden">
|
||||
<flux:navbar.item icon="layout-grid" :href="route('dashboard')" :current="request()->routeIs('dashboard')" wire:navigate>
|
||||
<flux:navbar class="-mb-px max-lg:hidden">
|
||||
<flux:navbar.item icon="layout-grid" :href="route('dashboard')" :current="request()->routeIs('dashboard')"
|
||||
wire:navigate>
|
||||
{{ __('Dashboard') }}
|
||||
</flux:navbar.item>
|
||||
</flux:navbar>
|
||||
|
||||
<flux:spacer />
|
||||
|
||||
<flux:navbar class="me-1.5 space-x-0.5 rtl:space-x-reverse py-0!">
|
||||
<flux:tooltip :content="__('Search')" position="bottom">
|
||||
<flux:navbar.item class="!h-10 [&>div>svg]:size-5" icon="magnifying-glass" href="#"
|
||||
:label="__('Search')" />
|
||||
</flux:tooltip>
|
||||
<flux:tooltip :content="__('Repository')" position="bottom">
|
||||
<flux:navbar.item class="h-10 max-lg:hidden [&>div>svg]:size-5" icon="folder-git-2"
|
||||
href="https://github.com/laravel/livewire-starter-kit" target="_blank" :label="__('Repository')" />
|
||||
</flux:tooltip>
|
||||
<flux:tooltip :content="__('Documentation')" position="bottom">
|
||||
<flux:navbar.item class="h-10 max-lg:hidden [&>div>svg]:size-5" icon="book-open-text"
|
||||
href="https://laravel.com/docs/starter-kits#livewire" target="_blank"
|
||||
:label="__('Documentation')" />
|
||||
</flux:tooltip>
|
||||
</flux:navbar>
|
||||
|
||||
<x-desktop-user-menu />
|
||||
</flux:header>
|
||||
|
||||
<!-- Mobile Menu -->
|
||||
<flux:sidebar collapsible="mobile" sticky
|
||||
class="lg:hidden border-e border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<flux:sidebar.header>
|
||||
<x-app-logo :sidebar="true" href="{{ route('dashboard') }}" wire:navigate />
|
||||
<flux:sidebar.collapse
|
||||
class="in-data-flux-sidebar-on-desktop:not-in-data-flux-sidebar-collapsed-desktop:-mr-2" />
|
||||
</flux:sidebar.header>
|
||||
|
||||
<flux:sidebar.nav>
|
||||
<flux:sidebar.group :heading="__('Platform')">
|
||||
<flux:sidebar.item icon="layout-grid" :href="route('dashboard')"
|
||||
:current="request()->routeIs('dashboard')" wire:navigate>
|
||||
{{ __('Dashboard') }}
|
||||
</flux:navbar.item>
|
||||
</flux:navbar>
|
||||
|
||||
<flux:spacer />
|
||||
|
||||
<flux:navbar class="me-1.5 space-x-0.5 rtl:space-x-reverse py-0!">
|
||||
<flux:tooltip :content="__('Search')" position="bottom">
|
||||
<flux:navbar.item class="!h-10 [&>div>svg]:size-5" icon="magnifying-glass" href="#" :label="__('Search')" />
|
||||
</flux:tooltip>
|
||||
<flux:tooltip :content="__('Repository')" position="bottom">
|
||||
<flux:navbar.item
|
||||
class="h-10 max-lg:hidden [&>div>svg]:size-5"
|
||||
icon="folder-git-2"
|
||||
href="https://github.com/laravel/livewire-starter-kit"
|
||||
target="_blank"
|
||||
:label="__('Repository')"
|
||||
/>
|
||||
</flux:tooltip>
|
||||
<flux:tooltip :content="__('Documentation')" position="bottom">
|
||||
<flux:navbar.item
|
||||
class="h-10 max-lg:hidden [&>div>svg]:size-5"
|
||||
icon="book-open-text"
|
||||
href="https://laravel.com/docs/starter-kits#livewire"
|
||||
target="_blank"
|
||||
:label="__('Documentation')"
|
||||
/>
|
||||
</flux:tooltip>
|
||||
</flux:navbar>
|
||||
|
||||
<x-desktop-user-menu />
|
||||
</flux:header>
|
||||
|
||||
<!-- Mobile Menu -->
|
||||
<flux:sidebar collapsible="mobile" sticky class="lg:hidden border-e border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<flux:sidebar.header>
|
||||
<x-app-logo :sidebar="true" href="{{ route('dashboard') }}" wire:navigate />
|
||||
<flux:sidebar.collapse class="in-data-flux-sidebar-on-desktop:not-in-data-flux-sidebar-collapsed-desktop:-mr-2" />
|
||||
</flux:sidebar.header>
|
||||
|
||||
<flux:sidebar.nav>
|
||||
<flux:sidebar.group :heading="__('Platform')">
|
||||
<flux:sidebar.item icon="layout-grid" :href="route('dashboard')" :current="request()->routeIs('dashboard')" wire:navigate>
|
||||
{{ __('Dashboard') }}
|
||||
</flux:sidebar.item>
|
||||
</flux:sidebar.group>
|
||||
</flux:sidebar.nav>
|
||||
|
||||
<flux:spacer />
|
||||
|
||||
<flux:sidebar.nav>
|
||||
<flux:sidebar.item icon="folder-git-2" href="https://github.com/laravel/livewire-starter-kit" target="_blank">
|
||||
{{ __('Repository') }}
|
||||
</flux:sidebar.item>
|
||||
<flux:sidebar.item icon="book-open-text" href="https://laravel.com/docs/starter-kits#livewire" target="_blank">
|
||||
{{ __('Documentation') }}
|
||||
</flux:sidebar.item>
|
||||
</flux:sidebar.nav>
|
||||
</flux:sidebar>
|
||||
</flux:sidebar.group>
|
||||
</flux:sidebar.nav>
|
||||
|
||||
{{ $slot }}
|
||||
<flux:spacer />
|
||||
|
||||
@persist('toast')
|
||||
<flux:toast.group>
|
||||
<flux:toast />
|
||||
</flux:toast.group>
|
||||
@endpersist
|
||||
<flux:sidebar.nav>
|
||||
<flux:sidebar.item icon="folder-git-2" href="https://github.com/laravel/livewire-starter-kit"
|
||||
target="_blank">
|
||||
{{ __('Repository') }}
|
||||
</flux:sidebar.item>
|
||||
<flux:sidebar.item icon="book-open-text" href="https://laravel.com/docs/starter-kits#livewire"
|
||||
target="_blank">
|
||||
{{ __('Documentation') }}
|
||||
</flux:sidebar.item>
|
||||
</flux:sidebar.nav>
|
||||
</flux:sidebar>
|
||||
|
||||
{{ $slot }}
|
||||
|
||||
@persist('toast')
|
||||
<flux:toast.group>
|
||||
<flux:toast />
|
||||
</flux:toast.group>
|
||||
@endpersist
|
||||
|
||||
@fluxScripts
|
||||
</body>
|
||||
|
||||
@fluxScripts
|
||||
</body>
|
||||
</html>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,34 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
|
||||
<head>
|
||||
@include('partials.head')
|
||||
</head>
|
||||
<body class="min-h-screen bg-neutral-100 antialiased dark:bg-linear-to-b dark:from-neutral-950 dark:to-neutral-900">
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);" aria-hidden="true"></div>
|
||||
<div class="bg-muted flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
|
||||
<div class="flex w-full max-w-md flex-col gap-6">
|
||||
<a href="{{ route('home') }}" class="flex flex-col items-center gap-2 font-medium" wire:navigate>
|
||||
<span class="flex h-9 w-9 items-center justify-center rounded-md">
|
||||
<x-app-logo-icon class="size-9 fill-current text-black dark:text-white" />
|
||||
</span>
|
||||
|
||||
<span class="sr-only">{{ config('app.name', 'Laravel') }}</span>
|
||||
</a>
|
||||
<head>
|
||||
@include('partials.head')
|
||||
</head>
|
||||
|
||||
<div class="flex flex-col gap-6">
|
||||
<div class="rounded-xl border bg-white dark:bg-stone-950 dark:border-stone-800 text-stone-800 shadow-xs">
|
||||
<div class="px-10 py-8">{{ $slot }}</div>
|
||||
</div>
|
||||
<body class="min-h-screen bg-neutral-100 antialiased dark:bg-linear-to-b dark:from-neutral-950 dark:to-neutral-900">
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
|
||||
aria-hidden="true"></div>
|
||||
<div class="bg-muted flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
|
||||
<div class="flex w-full max-w-md flex-col gap-6">
|
||||
<a href="{{ route('home') }}" class="flex flex-col items-center gap-2 font-medium" wire:navigate>
|
||||
<span class="flex h-9 w-9 items-center justify-center rounded-md">
|
||||
<x-app-logo-icon class="size-9 fill-current text-black dark:text-white" />
|
||||
</span>
|
||||
|
||||
<span class="sr-only">{{ config('app.name', 'Laravel') }}</span>
|
||||
</a>
|
||||
|
||||
<div class="flex flex-col gap-6">
|
||||
<div
|
||||
class="rounded-xl border bg-white dark:bg-stone-950 dark:border-stone-800 text-stone-800 shadow-xs">
|
||||
<div class="px-10 py-8">{{ $slot }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@persist('toast')
|
||||
<flux:toast.group>
|
||||
<flux:toast />
|
||||
</flux:toast.group>
|
||||
@endpersist
|
||||
@persist('toast')
|
||||
<flux:toast.group>
|
||||
<flux:toast />
|
||||
</flux:toast.group>
|
||||
@endpersist
|
||||
|
||||
@fluxScripts
|
||||
</body>
|
||||
|
||||
@fluxScripts
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
|
||||
<head>
|
||||
@include('partials.head')
|
||||
</head>
|
||||
<body class="min-h-screen bg-white antialiased dark:bg-linear-to-b dark:from-neutral-950 dark:to-neutral-900">
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);" aria-hidden="true"></div>
|
||||
<div class="bg-background flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
|
||||
<div class="flex w-full max-w-sm flex-col gap-2">
|
||||
<a href="{{ route('home') }}" class="flex flex-col items-center gap-2 font-medium" wire:navigate>
|
||||
<span class="flex h-9 w-9 mb-1 items-center justify-center rounded-md">
|
||||
<x-app-logo-icon class="size-9 fill-current text-black dark:text-white" />
|
||||
</span>
|
||||
<span class="sr-only">{{ config('app.name', 'Laravel') }}</span>
|
||||
</a>
|
||||
<div class="flex flex-col gap-6">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
||||
<head>
|
||||
@include('partials.head')
|
||||
</head>
|
||||
|
||||
<body class="min-h-screen bg-white antialiased dark:bg-linear-to-b dark:from-neutral-950 dark:to-neutral-900">
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
|
||||
aria-hidden="true"></div>
|
||||
<div class="bg-background flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
|
||||
<div class="flex w-full max-w-sm flex-col gap-2">
|
||||
<a href="{{ route('home') }}" class="flex flex-col items-center gap-2 font-medium" wire:navigate>
|
||||
<span class="flex h-9 w-9 mb-1 items-center justify-center rounded-md">
|
||||
<x-app-logo-icon class="size-9 fill-current text-black dark:text-white" />
|
||||
</span>
|
||||
<span class="sr-only">{{ config('app.name', 'Laravel') }}</span>
|
||||
</a>
|
||||
<div class="flex flex-col gap-6">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@persist('toast')
|
||||
<flux:toast.group>
|
||||
<flux:toast />
|
||||
</flux:toast.group>
|
||||
@endpersist
|
||||
@persist('toast')
|
||||
<flux:toast.group>
|
||||
<flux:toast />
|
||||
</flux:toast.group>
|
||||
@endpersist
|
||||
|
||||
@fluxScripts
|
||||
</body>
|
||||
|
||||
@fluxScripts
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,51 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
|
||||
<head>
|
||||
@include('partials.head')
|
||||
</head>
|
||||
<body class="min-h-screen bg-white antialiased dark:bg-linear-to-b dark:from-neutral-950 dark:to-neutral-900">
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);" aria-hidden="true"></div>
|
||||
<div class="relative grid h-dvh flex-col items-center justify-center px-8 sm:px-0 lg:max-w-none lg:grid-cols-2 lg:px-0">
|
||||
<div class="bg-muted relative hidden h-full flex-col p-10 text-white lg:flex dark:border-e dark:border-neutral-800">
|
||||
<div class="absolute inset-0 bg-neutral-900"></div>
|
||||
<a href="{{ route('home') }}" class="relative z-20 flex items-center text-lg font-medium" wire:navigate>
|
||||
<span class="flex h-10 w-10 items-center justify-center rounded-md">
|
||||
<x-app-logo-icon class="me-2 h-7 fill-current text-white" />
|
||||
</span>
|
||||
{{ config('app.name', 'Laravel') }}
|
||||
</a>
|
||||
|
||||
@php
|
||||
[$message, $author] = str(Illuminate\Foundation\Inspiring::quotes()->random())->explode('-');
|
||||
@endphp
|
||||
<head>
|
||||
@include('partials.head')
|
||||
</head>
|
||||
|
||||
<div class="relative z-20 mt-auto">
|
||||
<blockquote class="space-y-2">
|
||||
<flux:heading size="lg">“{{ trim($message) }}”</flux:heading>
|
||||
<footer><flux:heading>{{ trim($author) }}</flux:heading></footer>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:p-8">
|
||||
<div class="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
|
||||
<a href="{{ route('home') }}" class="z-20 flex flex-col items-center gap-2 font-medium lg:hidden" wire:navigate>
|
||||
<span class="flex h-9 w-9 items-center justify-center rounded-md">
|
||||
<x-app-logo-icon class="size-9 fill-current text-black dark:text-white" />
|
||||
</span>
|
||||
<body class="min-h-screen bg-white antialiased dark:bg-linear-to-b dark:from-neutral-950 dark:to-neutral-900">
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
|
||||
aria-hidden="true"></div>
|
||||
<div
|
||||
class="relative grid h-dvh flex-col items-center justify-center px-8 sm:px-0 lg:max-w-none lg:grid-cols-2 lg:px-0">
|
||||
<div
|
||||
class="bg-muted relative hidden h-full flex-col p-10 text-white lg:flex dark:border-e dark:border-neutral-800">
|
||||
<div class="absolute inset-0 bg-neutral-900"></div>
|
||||
<a href="{{ route('home') }}" class="relative z-20 flex items-center text-lg font-medium" wire:navigate>
|
||||
<span class="flex h-10 w-10 items-center justify-center rounded-md">
|
||||
<x-app-logo-icon class="me-2 h-7 fill-current text-white" />
|
||||
</span>
|
||||
{{ config('app.name', 'Laravel') }}
|
||||
</a>
|
||||
|
||||
<span class="sr-only">{{ config('app.name', 'Laravel') }}</span>
|
||||
</a>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
@php
|
||||
[$message, $author] = str(Illuminate\Foundation\Inspiring::quotes()->random())->explode('-');
|
||||
@endphp
|
||||
|
||||
<div class="relative z-20 mt-auto">
|
||||
<blockquote class="space-y-2">
|
||||
<flux:heading size="lg">“{{ trim($message) }}”</flux:heading>
|
||||
<footer>
|
||||
<flux:heading>{{ trim($author) }}</flux:heading>
|
||||
</footer>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:p-8">
|
||||
<div class="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
|
||||
<a href="{{ route('home') }}" class="z-20 flex flex-col items-center gap-2 font-medium lg:hidden"
|
||||
wire:navigate>
|
||||
<span class="flex h-9 w-9 items-center justify-center rounded-md">
|
||||
<x-app-logo-icon class="size-9 fill-current text-black dark:text-white" />
|
||||
</span>
|
||||
|
||||
@persist('toast')
|
||||
<flux:toast.group>
|
||||
<flux:toast />
|
||||
</flux:toast.group>
|
||||
@endpersist
|
||||
<span class="sr-only">{{ config('app.name', 'Laravel') }}</span>
|
||||
</a>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@persist('toast')
|
||||
<flux:toast.group>
|
||||
<flux:toast />
|
||||
</flux:toast.group>
|
||||
@endpersist
|
||||
|
||||
@fluxScripts
|
||||
</body>
|
||||
|
||||
@fluxScripts
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
{{-- Shared Application Top Bar --}}
|
||||
@php
|
||||
$user = auth()->user();
|
||||
$dashboardRoute = Route::has('platform.dashboard') && method_exists($user, 'isMpbaPlatformAdmin') && $user?->isMpbaPlatformAdmin()
|
||||
? 'platform.dashboard'
|
||||
: (Route::has('dashboard') ? 'dashboard' : null);
|
||||
$dashboardRoute =
|
||||
Route::has('platform.dashboard') && method_exists($user, 'isMpbaPlatformAdmin') && $user?->isMpbaPlatformAdmin()
|
||||
? 'platform.dashboard'
|
||||
: (Route::has('dashboard')
|
||||
? 'dashboard'
|
||||
: null);
|
||||
@endphp
|
||||
|
||||
<header class="sticky top-0 z-40 border-b border-white/10 bg-[#111118]/95 backdrop-blur" role="banner">
|
||||
<div class="flex h-16 w-full items-center justify-between gap-3 px-3 sm:px-4 lg:px-6">
|
||||
<div class="flex min-w-0 items-center gap-3 lg:hidden">
|
||||
<button
|
||||
type="button"
|
||||
<button type="button"
|
||||
class="inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-xl border border-white/10 bg-white/5 text-white transition hover:bg-white/10 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-offset-2 focus:ring-offset-[#111118] lg:hidden"
|
||||
data-wms-mobile-open
|
||||
aria-label="{{ __('Open navigation menu') }}"
|
||||
aria-controls="wms-main-sidebar"
|
||||
aria-expanded="false"
|
||||
data-test="mobile-menu-button"
|
||||
>
|
||||
data-wms-mobile-open aria-label="{{ __('Open navigation menu') }}" aria-controls="wms-main-sidebar"
|
||||
aria-expanded="false" data-test="mobile-menu-button">
|
||||
<i class="fa-solid fa-bars" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<a
|
||||
href="{{ $dashboardRoute ? route($dashboardRoute) : '#' }}"
|
||||
wire:navigate
|
||||
class="flex min-w-0 items-center gap-3"
|
||||
aria-label="{{ __('Warehouse Management System') }}"
|
||||
>
|
||||
<span class="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-cyan-600 text-sm font-bold text-white shadow-lg shadow-cyan-950/40">
|
||||
<a href="{{ $dashboardRoute ? route($dashboardRoute) : '#' }}" wire:navigate
|
||||
class="flex min-w-0 items-center gap-3" aria-label="{{ __('Warehouse Management System') }}">
|
||||
<span
|
||||
class="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-cyan-600 text-sm font-bold text-white shadow-lg shadow-cyan-950/40">
|
||||
W
|
||||
</span>
|
||||
|
||||
@@ -42,67 +37,64 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="userControls"
|
||||
class="relative ml-auto flex shrink-0 items-center gap-3"
|
||||
x-data="{ open: false }"
|
||||
x-on:keydown.escape.window="open = false"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
<div id="userControls" class="relative ml-auto flex shrink-0 items-center gap-3" x-data="{ open: false }"
|
||||
x-on:keydown.escape.window="open = false">
|
||||
<button type="button"
|
||||
class="flex items-center gap-3 rounded-2xl border border-white/10 bg-white/5 px-2 py-2 text-left transition hover:bg-white/10 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-offset-2 focus:ring-offset-[#111118] sm:px-3"
|
||||
x-on:click="open = ! open"
|
||||
x-bind:aria-expanded="open.toString()"
|
||||
aria-haspopup="menu"
|
||||
data-test="top-user-menu-button"
|
||||
>
|
||||
x-on:click="open = ! open" x-bind:aria-expanded="open.toString()" aria-haspopup="menu"
|
||||
data-test="top-user-menu-button">
|
||||
<span class="hidden text-right md:block">
|
||||
<span class="block max-w-48 truncate text-sm font-semibold text-white">{{ $user?->name ?? __('User') }}</span>
|
||||
<span
|
||||
class="block max-w-48 truncate text-sm font-semibold text-white">{{ $user?->name ?? __('User') }}</span>
|
||||
<span class="block max-w-48 truncate text-xs text-white/50">{{ $user?->email ?? '' }}</span>
|
||||
</span>
|
||||
|
||||
<span class="flex h-10 w-10 items-center justify-center rounded-full bg-blue-700 text-xs font-bold text-white shadow-lg shadow-blue-950/40">
|
||||
<span
|
||||
class="flex h-10 w-10 items-center justify-center rounded-full bg-blue-700 text-xs font-bold text-white shadow-lg shadow-blue-950/40">
|
||||
{{ $user?->initials() ?? 'U' }}
|
||||
</span>
|
||||
|
||||
<i class="fa-solid fa-chevron-down hidden text-white/50 sm:block" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<div
|
||||
x-cloak
|
||||
x-show="open"
|
||||
x-transition.origin.top.right
|
||||
x-on:click.outside="open = false"
|
||||
<div x-cloak x-show="open" x-transition.origin.top.right x-on:click.outside="open = false"
|
||||
class="absolute right-0 top-full mt-2 w-[calc(100vw-1.5rem)] max-w-72 overflow-hidden rounded-2xl border border-white/10 bg-[#181820] shadow-2xl shadow-black/40 sm:w-72"
|
||||
role="menu"
|
||||
>
|
||||
role="menu">
|
||||
<div class="border-b border-white/10 px-4 py-3">
|
||||
<div class="truncate text-sm font-semibold text-white">{{ $user?->name ?? __('User') }}</div>
|
||||
<div class="truncate text-xs text-white/50">{{ $user?->email ?? '' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="p-2">
|
||||
@if($dashboardRoute)
|
||||
<a href="{{ route($dashboardRoute) }}" wire:navigate class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white" role="menuitem">
|
||||
@if ($dashboardRoute)
|
||||
<a href="{{ route($dashboardRoute) }}" wire:navigate
|
||||
class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white"
|
||||
role="menuitem">
|
||||
{{ __('Dashboard') }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if(Route::has('profile.edit'))
|
||||
<a href="{{ route('profile.edit') }}" wire:navigate class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white" role="menuitem">
|
||||
@if (Route::has('profile.edit'))
|
||||
<a href="{{ route('profile.edit') }}" wire:navigate
|
||||
class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white"
|
||||
role="menuitem">
|
||||
{{ __('Profile') }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if(Route::has('settings.index'))
|
||||
<a href="{{ route('settings.index') }}" wire:navigate class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white" role="menuitem">
|
||||
@if (Route::has('settings.index'))
|
||||
<a href="{{ route('settings.index') }}" wire:navigate
|
||||
class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white"
|
||||
role="menuitem">
|
||||
{{ __('Settings') }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('logout') }}" class="mt-2 border-t border-white/10 pt-2">
|
||||
@csrf
|
||||
<button type="submit" class="w-full rounded-xl px-3 py-2 text-left text-sm text-red-300 hover:bg-red-500/10 hover:text-red-200" role="menuitem" data-test="logout-button">
|
||||
<button type="submit"
|
||||
class="w-full rounded-xl px-3 py-2 text-left text-sm text-red-300 hover:bg-red-500/10 hover:text-red-200"
|
||||
role="menuitem" data-test="logout-button">
|
||||
{{ __('Sign out') }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -1,42 +1,38 @@
|
||||
<x-layouts::auth :title="__('Confirm password')">
|
||||
<section class="mx-auto w-full max-w-md overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-xl shadow-zinc-900/10 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<section
|
||||
class="mx-auto w-full max-w-md overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-xl shadow-zinc-900/10 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<div class="h-1.5 bg-gradient-to-r from-emerald-500 via-sky-500 to-blue-600"></div>
|
||||
|
||||
<div class="space-y-7 p-6 sm:p-8">
|
||||
<div class="space-y-5 text-center">
|
||||
<div class="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-zinc-950 text-sm font-black text-white dark:bg-white dark:text-zinc-950">WMS</div>
|
||||
<div
|
||||
class="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-zinc-950 text-sm font-black text-white dark:bg-white dark:text-zinc-950">
|
||||
WMS</div>
|
||||
<div class="space-y-2">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-sky-700 dark:text-sky-300">{{ __('Protected area') }}</p>
|
||||
<h1 class="text-2xl font-bold tracking-tight text-zinc-950 dark:text-white">{{ __('Confirm password') }}</h1>
|
||||
<p class="text-sm leading-6 text-zinc-600 dark:text-zinc-400">{{ __('Confirm your identity before continuing to this secure WMS workflow.') }}</p>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-sky-700 dark:text-sky-300">
|
||||
{{ __('Protected area') }}</p>
|
||||
<h1 class="text-2xl font-bold tracking-tight text-zinc-950 dark:text-white">
|
||||
{{ __('Confirm password') }}</h1>
|
||||
<p class="text-sm leading-6 text-zinc-600 dark:text-zinc-400">
|
||||
{{ __('Confirm your identity before continuing to this secure WMS workflow.') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-zinc-200 bg-zinc-50 p-5 dark:border-zinc-800 dark:bg-zinc-900/60">
|
||||
<x-auth-session-status class="mb-5 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-center text-sm text-emerald-800 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-200" :status="session('status')" />
|
||||
<x-auth-session-status
|
||||
class="mb-5 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-center text-sm text-emerald-800 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-200"
|
||||
:status="session('status')" />
|
||||
|
||||
<div class="mb-5">
|
||||
<x-passkey-verify
|
||||
options-route="passkey.confirm-options"
|
||||
submit-route="passkey.confirm"
|
||||
:label="__('Confirm with passkey')"
|
||||
:loading-label="__('Confirming...')"
|
||||
:separator="__('Or confirm with password')"
|
||||
/>
|
||||
<x-passkey-verify options-route="passkey.confirm-options" submit-route="passkey.confirm"
|
||||
:label="__('Confirm with passkey')" :loading-label="__('Confirming...')" :separator="__('Or confirm with password')" />
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('password.confirm.store') }}" class="flex flex-col gap-5">
|
||||
@csrf
|
||||
|
||||
<flux:input
|
||||
name="password"
|
||||
:label="__('Password')"
|
||||
type="password"
|
||||
required
|
||||
autocomplete="current-password"
|
||||
:placeholder="__('Password')"
|
||||
viewable
|
||||
/>
|
||||
<flux:input name="password" :label="__('Password')" type="password" required
|
||||
autocomplete="current-password" :placeholder="__('Password')" viewable />
|
||||
|
||||
<flux:button variant="primary" type="submit" class="w-full" data-test="confirm-password-button">
|
||||
{{ __('Confirm') }}
|
||||
|
||||
@@ -1,35 +1,38 @@
|
||||
<x-layouts::auth :title="__('Forgot password')">
|
||||
<section class="mx-auto w-full max-w-md overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-xl shadow-zinc-900/10 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<section
|
||||
class="mx-auto w-full max-w-md overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-xl shadow-zinc-900/10 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<div class="h-1.5 bg-gradient-to-r from-emerald-500 via-sky-500 to-blue-600"></div>
|
||||
|
||||
<div class="space-y-7 p-6 sm:p-8">
|
||||
<div class="space-y-5 text-center">
|
||||
<div class="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-zinc-950 text-sm font-black text-white dark:bg-white dark:text-zinc-950">WMS</div>
|
||||
<div
|
||||
class="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-zinc-950 text-sm font-black text-white dark:bg-white dark:text-zinc-950">
|
||||
WMS</div>
|
||||
<div class="space-y-2">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-sky-700 dark:text-sky-300">{{ __('Account recovery') }}</p>
|
||||
<h1 class="text-2xl font-bold tracking-tight text-zinc-950 dark:text-white">{{ __('Warehouse Management System') }}</h1>
|
||||
<p class="text-sm leading-6 text-zinc-600 dark:text-zinc-400">{{ __('Request a secure reset link for your WMS account.') }}</p>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-sky-700 dark:text-sky-300">
|
||||
{{ __('Account recovery') }}</p>
|
||||
<h1 class="text-2xl font-bold tracking-tight text-zinc-950 dark:text-white">
|
||||
{{ __('Warehouse Management System') }}</h1>
|
||||
<p class="text-sm leading-6 text-zinc-600 dark:text-zinc-400">
|
||||
{{ __('Request a secure reset link for your WMS account.') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-zinc-200 bg-zinc-50 p-5 dark:border-zinc-800 dark:bg-zinc-900/60">
|
||||
<x-auth-header :title="__('Forgot password')" :description="__('Enter your email address and we will send you a password reset link.')" />
|
||||
|
||||
<x-auth-session-status class="mt-5 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-center text-sm text-emerald-800 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-200" :status="session('status')" />
|
||||
<x-auth-session-status
|
||||
class="mt-5 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-center text-sm text-emerald-800 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-200"
|
||||
:status="session('status')" />
|
||||
|
||||
<form method="POST" action="{{ route('password.email') }}" class="mt-6 flex flex-col gap-5">
|
||||
@csrf
|
||||
|
||||
<flux:input
|
||||
name="email"
|
||||
:label="__('Email address')"
|
||||
type="email"
|
||||
required
|
||||
autofocus
|
||||
placeholder="email@example.com"
|
||||
/>
|
||||
<flux:input name="email" :label="__('Email address')" type="email" required autofocus
|
||||
placeholder="email@example.com" />
|
||||
|
||||
<flux:button variant="primary" type="submit" class="w-full" data-test="email-password-reset-link-button">
|
||||
<flux:button variant="primary" type="submit" class="w-full"
|
||||
data-test="email-password-reset-link-button">
|
||||
{{ __('Email password reset link') }}
|
||||
</flux:button>
|
||||
</form>
|
||||
|
||||
@@ -1,28 +1,38 @@
|
||||
<x-layouts::auth :title="__('Log in')">
|
||||
<section class="mx-auto w-full max-w-md overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-xl shadow-zinc-900/10 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<section
|
||||
class="mx-auto w-full max-w-md overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-xl shadow-zinc-900/10 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<div class="h-1.5 bg-gradient-to-r from-emerald-500 via-sky-500 to-blue-600"></div>
|
||||
|
||||
<div class="space-y-7 p-6 sm:p-8">
|
||||
<div class="space-y-5 text-center">
|
||||
<div class="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-zinc-950 text-sm font-black tracking-tight text-white shadow-lg shadow-zinc-900/20 dark:bg-white dark:text-zinc-950">
|
||||
<div
|
||||
class="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-zinc-950 text-sm font-black tracking-tight text-white shadow-lg shadow-zinc-900/20 dark:bg-white dark:text-zinc-950">
|
||||
WMS
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-sky-700 dark:text-sky-300">MPBA Limited</p>
|
||||
<h1 class="text-2xl font-bold tracking-tight text-zinc-950 dark:text-white">{{ __('Warehouse Management System') }}</h1>
|
||||
<p class="text-sm leading-6 text-zinc-600 dark:text-zinc-400">{{ __('Secure access for tenant workspaces, warehouse operations, support, licences, and dashboards.') }}</p>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-sky-700 dark:text-sky-300">MPBA
|
||||
Limited</p>
|
||||
<h1 class="text-2xl font-bold tracking-tight text-zinc-950 dark:text-white">
|
||||
{{ __('Warehouse Management System') }}</h1>
|
||||
<p class="text-sm leading-6 text-zinc-600 dark:text-zinc-400">
|
||||
{{ __('Secure access for tenant workspaces, warehouse operations, support, licences, and dashboards.') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-zinc-200 bg-zinc-50 p-5 dark:border-zinc-800 dark:bg-zinc-900/60">
|
||||
<div class="mb-6 space-y-1 text-center">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.22em] text-sky-700 dark:text-sky-300">{{ __('Secure sign in') }}</p>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.22em] text-sky-700 dark:text-sky-300">
|
||||
{{ __('Secure sign in') }}</p>
|
||||
<h2 class="text-xl font-bold text-zinc-950 dark:text-white">{{ __('Log in to your account') }}</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-400">{{ __('Enter your email and password to continue.') }}</p>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-400">
|
||||
{{ __('Enter your email and password to continue.') }}</p>
|
||||
</div>
|
||||
|
||||
<x-auth-session-status class="mb-5 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-center text-sm text-emerald-800 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-200" :status="session('status')" />
|
||||
<x-auth-session-status
|
||||
class="mb-5 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-center text-sm text-emerald-800 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-200"
|
||||
:status="session('status')" />
|
||||
|
||||
<div class="mb-5">
|
||||
<x-passkey-verify />
|
||||
@@ -32,28 +42,44 @@
|
||||
@csrf
|
||||
|
||||
<div class="grid gap-2">
|
||||
<label for="email" class="text-sm font-semibold text-zinc-800 dark:text-zinc-200">{{ __('Email address') }}</label>
|
||||
<input id="email" name="email" type="email" value="{{ old('email') }}" required autofocus autocomplete="email" placeholder="email@example.com" class="w-full rounded-xl border border-zinc-300 bg-white px-4 py-3 text-sm text-zinc-950 shadow-sm outline-none transition placeholder:text-zinc-400 focus:border-sky-500 focus:ring-4 focus:ring-sky-100 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white dark:placeholder:text-zinc-500 dark:focus:border-sky-400 dark:focus:ring-sky-950" />
|
||||
@error('email')<p class="text-sm font-medium text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
|
||||
<label for="email"
|
||||
class="text-sm font-semibold text-zinc-800 dark:text-zinc-200">{{ __('Email address') }}</label>
|
||||
<input id="email" name="email" type="email" value="{{ old('email') }}" required
|
||||
autofocus autocomplete="email" placeholder="email@example.com"
|
||||
class="w-full rounded-xl border border-zinc-300 bg-white px-4 py-3 text-sm text-zinc-950 shadow-sm outline-none transition placeholder:text-zinc-400 focus:border-sky-500 focus:ring-4 focus:ring-sky-100 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white dark:placeholder:text-zinc-500 dark:focus:border-sky-400 dark:focus:ring-sky-950" />
|
||||
@error('email')
|
||||
<p class="text-sm font-medium text-red-600 dark:text-red-400">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<label for="password" class="text-sm font-semibold text-zinc-800 dark:text-zinc-200">{{ __('Password') }}</label>
|
||||
<label for="password"
|
||||
class="text-sm font-semibold text-zinc-800 dark:text-zinc-200">{{ __('Password') }}</label>
|
||||
@if (Route::has('password.request'))
|
||||
<a class="text-sm font-semibold text-sky-700 underline underline-offset-4 hover:text-sky-900 dark:text-sky-300" href="{{ route('password.request') }}" wire:navigate>{{ __('Forgot password?') }}</a>
|
||||
<a class="text-sm font-semibold text-sky-700 underline underline-offset-4 hover:text-sky-900 dark:text-sky-300"
|
||||
href="{{ route('password.request') }}"
|
||||
wire:navigate>{{ __('Forgot password?') }}</a>
|
||||
@endif
|
||||
</div>
|
||||
<input id="password" name="password" type="password" required autocomplete="current-password" placeholder="{{ __('Password') }}" class="w-full rounded-xl border border-zinc-300 bg-white px-4 py-3 text-sm text-zinc-950 shadow-sm outline-none transition placeholder:text-zinc-400 focus:border-sky-500 focus:ring-4 focus:ring-sky-100 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white dark:placeholder:text-zinc-500 dark:focus:border-sky-400 dark:focus:ring-sky-950" />
|
||||
@error('password')<p class="text-sm font-medium text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
|
||||
<input id="password" name="password" type="password" required autocomplete="current-password"
|
||||
placeholder="{{ __('Password') }}"
|
||||
class="w-full rounded-xl border border-zinc-300 bg-white px-4 py-3 text-sm text-zinc-950 shadow-sm outline-none transition placeholder:text-zinc-400 focus:border-sky-500 focus:ring-4 focus:ring-sky-100 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white dark:placeholder:text-zinc-500 dark:focus:border-sky-400 dark:focus:ring-sky-950" />
|
||||
@error('password')
|
||||
<p class="text-sm font-medium text-red-600 dark:text-red-400">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<label class="flex items-center gap-3 rounded-xl border border-zinc-200 bg-white px-4 py-3 text-sm text-zinc-700 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-300">
|
||||
<input name="remember" type="checkbox" value="1" @checked(old('remember')) class="rounded border-zinc-300 text-sky-600 focus:ring-sky-500 dark:border-zinc-700 dark:bg-zinc-950" />
|
||||
<label
|
||||
class="flex items-center gap-3 rounded-xl border border-zinc-200 bg-white px-4 py-3 text-sm text-zinc-700 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-300">
|
||||
<input name="remember" type="checkbox" value="1" @checked(old('remember'))
|
||||
class="rounded border-zinc-300 text-sky-600 focus:ring-sky-500 dark:border-zinc-700 dark:bg-zinc-950" />
|
||||
<span>{{ __('Remember this device') }}</span>
|
||||
</label>
|
||||
|
||||
<button type="submit" class="w-full rounded-xl bg-zinc-950 px-4 py-3 text-sm font-bold text-white shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-4 focus:ring-sky-200 dark:bg-white dark:text-zinc-950 dark:hover:bg-zinc-200 dark:focus:ring-sky-950" data-test="login-button">
|
||||
<button type="submit"
|
||||
class="w-full rounded-xl bg-zinc-950 px-4 py-3 text-sm font-bold text-white shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-4 focus:ring-sky-200 dark:bg-white dark:text-zinc-950 dark:hover:bg-zinc-200 dark:focus:ring-sky-950"
|
||||
data-test="login-button">
|
||||
{{ __('Log in') }}
|
||||
</button>
|
||||
</form>
|
||||
@@ -61,7 +87,8 @@
|
||||
|
||||
<div class="text-center text-sm text-zinc-600 dark:text-zinc-400">
|
||||
<span>{{ __('Need access?') }}</span>
|
||||
<a href="{{ route('register') }}" wire:navigate class="font-semibold text-sky-700 underline underline-offset-4 hover:text-sky-900 dark:text-sky-300">{{ __('Create an account') }}</a>
|
||||
<a href="{{ route('register') }}" wire:navigate
|
||||
class="font-semibold text-sky-700 underline underline-offset-4 hover:text-sky-900 dark:text-sky-300">{{ __('Create an account') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,50 +1,77 @@
|
||||
<x-layouts::auth :title="__('Register')">
|
||||
<section class="mx-auto w-full max-w-md overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-xl shadow-zinc-900/10 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<section
|
||||
class="mx-auto w-full max-w-md overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-xl shadow-zinc-900/10 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<div class="h-1.5 bg-gradient-to-r from-emerald-500 via-sky-500 to-blue-600"></div>
|
||||
|
||||
<div class="space-y-7 p-6 sm:p-8">
|
||||
<div class="space-y-5 text-center">
|
||||
<div class="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-zinc-950 text-sm font-black tracking-tight text-white shadow-lg shadow-zinc-900/20 dark:bg-white dark:text-zinc-950">
|
||||
<div
|
||||
class="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-zinc-950 text-sm font-black tracking-tight text-white shadow-lg shadow-zinc-900/20 dark:bg-white dark:text-zinc-950">
|
||||
WMS
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-sky-700 dark:text-sky-300">{{ __('MPBA Limited') }}</p>
|
||||
<h1 class="text-2xl font-bold tracking-tight text-zinc-950 dark:text-white">{{ __('Create your WMS account') }}</h1>
|
||||
<p class="text-sm leading-6 text-zinc-600 dark:text-zinc-400">{{ __('Register for secure access to your company workspace and warehouse operations.') }}</p>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-sky-700 dark:text-sky-300">
|
||||
{{ __('MPBA Limited') }}</p>
|
||||
<h1 class="text-2xl font-bold tracking-tight text-zinc-950 dark:text-white">
|
||||
{{ __('Create your WMS account') }}</h1>
|
||||
<p class="text-sm leading-6 text-zinc-600 dark:text-zinc-400">
|
||||
{{ __('Register for secure access to your company workspace and warehouse operations.') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-zinc-200 bg-zinc-50 p-5 dark:border-zinc-800 dark:bg-zinc-900/60">
|
||||
<x-auth-session-status class="mb-5 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-center text-sm text-emerald-800 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-200" :status="session('status')" />
|
||||
<x-auth-session-status
|
||||
class="mb-5 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-center text-sm text-emerald-800 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-200"
|
||||
:status="session('status')" />
|
||||
|
||||
<form method="POST" action="{{ route('register.store') }}" class="flex flex-col gap-5">
|
||||
@csrf
|
||||
|
||||
<div class="grid gap-2">
|
||||
<label for="name" class="text-sm font-semibold text-zinc-800 dark:text-zinc-200">{{ __('Name') }}</label>
|
||||
<input id="name" name="name" type="text" value="{{ old('name') }}" required autofocus autocomplete="name" placeholder="{{ __('Full name') }}" class="w-full rounded-xl border border-zinc-300 bg-white px-4 py-3 text-sm text-zinc-950 shadow-sm outline-none transition placeholder:text-zinc-400 focus:border-sky-500 focus:ring-4 focus:ring-sky-100 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white dark:placeholder:text-zinc-500 dark:focus:border-sky-400 dark:focus:ring-sky-950" />
|
||||
@error('name')<p class="text-sm font-medium text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
|
||||
<label for="name"
|
||||
class="text-sm font-semibold text-zinc-800 dark:text-zinc-200">{{ __('Name') }}</label>
|
||||
<input id="name" name="name" type="text" value="{{ old('name') }}" required
|
||||
autofocus autocomplete="name" placeholder="{{ __('Full name') }}"
|
||||
class="w-full rounded-xl border border-zinc-300 bg-white px-4 py-3 text-sm text-zinc-950 shadow-sm outline-none transition placeholder:text-zinc-400 focus:border-sky-500 focus:ring-4 focus:ring-sky-100 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white dark:placeholder:text-zinc-500 dark:focus:border-sky-400 dark:focus:ring-sky-950" />
|
||||
@error('name')
|
||||
<p class="text-sm font-medium text-red-600 dark:text-red-400">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<label for="email" class="text-sm font-semibold text-zinc-800 dark:text-zinc-200">{{ __('Email address') }}</label>
|
||||
<input id="email" name="email" type="email" value="{{ old('email') }}" required autocomplete="email" placeholder="email@example.com" class="w-full rounded-xl border border-zinc-300 bg-white px-4 py-3 text-sm text-zinc-950 shadow-sm outline-none transition placeholder:text-zinc-400 focus:border-sky-500 focus:ring-4 focus:ring-sky-100 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white dark:placeholder:text-zinc-500 dark:focus:border-sky-400 dark:focus:ring-sky-950" />
|
||||
@error('email')<p class="text-sm font-medium text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
|
||||
<label for="email"
|
||||
class="text-sm font-semibold text-zinc-800 dark:text-zinc-200">{{ __('Email address') }}</label>
|
||||
<input id="email" name="email" type="email" value="{{ old('email') }}" required
|
||||
autocomplete="email" placeholder="email@example.com"
|
||||
class="w-full rounded-xl border border-zinc-300 bg-white px-4 py-3 text-sm text-zinc-950 shadow-sm outline-none transition placeholder:text-zinc-400 focus:border-sky-500 focus:ring-4 focus:ring-sky-100 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white dark:placeholder:text-zinc-500 dark:focus:border-sky-400 dark:focus:ring-sky-950" />
|
||||
@error('email')
|
||||
<p class="text-sm font-medium text-red-600 dark:text-red-400">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<label for="password" class="text-sm font-semibold text-zinc-800 dark:text-zinc-200">{{ __('Password') }}</label>
|
||||
<input id="password" name="password" type="password" required autocomplete="new-password" placeholder="{{ __('Password') }}" class="w-full rounded-xl border border-zinc-300 bg-white px-4 py-3 text-sm text-zinc-950 shadow-sm outline-none transition placeholder:text-zinc-400 focus:border-sky-500 focus:ring-4 focus:ring-sky-100 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white dark:placeholder:text-zinc-500 dark:focus:border-sky-400 dark:focus:ring-sky-950" />
|
||||
@error('password')<p class="text-sm font-medium text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
|
||||
<label for="password"
|
||||
class="text-sm font-semibold text-zinc-800 dark:text-zinc-200">{{ __('Password') }}</label>
|
||||
<input id="password" name="password" type="password" required autocomplete="new-password"
|
||||
placeholder="{{ __('Password') }}"
|
||||
class="w-full rounded-xl border border-zinc-300 bg-white px-4 py-3 text-sm text-zinc-950 shadow-sm outline-none transition placeholder:text-zinc-400 focus:border-sky-500 focus:ring-4 focus:ring-sky-100 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white dark:placeholder:text-zinc-500 dark:focus:border-sky-400 dark:focus:ring-sky-950" />
|
||||
@error('password')
|
||||
<p class="text-sm font-medium text-red-600 dark:text-red-400">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<label for="password_confirmation" class="text-sm font-semibold text-zinc-800 dark:text-zinc-200">{{ __('Confirm password') }}</label>
|
||||
<input id="password_confirmation" name="password_confirmation" type="password" required autocomplete="new-password" placeholder="{{ __('Confirm password') }}" class="w-full rounded-xl border border-zinc-300 bg-white px-4 py-3 text-sm text-zinc-950 shadow-sm outline-none transition placeholder:text-zinc-400 focus:border-sky-500 focus:ring-4 focus:ring-sky-100 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white dark:placeholder:text-zinc-500 dark:focus:border-sky-400 dark:focus:ring-sky-950" />
|
||||
<label for="password_confirmation"
|
||||
class="text-sm font-semibold text-zinc-800 dark:text-zinc-200">{{ __('Confirm password') }}</label>
|
||||
<input id="password_confirmation" name="password_confirmation" type="password" required
|
||||
autocomplete="new-password" placeholder="{{ __('Confirm password') }}"
|
||||
class="w-full rounded-xl border border-zinc-300 bg-white px-4 py-3 text-sm text-zinc-950 shadow-sm outline-none transition placeholder:text-zinc-400 focus:border-sky-500 focus:ring-4 focus:ring-sky-100 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white dark:placeholder:text-zinc-500 dark:focus:border-sky-400 dark:focus:ring-sky-950" />
|
||||
</div>
|
||||
|
||||
<button type="submit" class="w-full rounded-xl bg-zinc-950 px-4 py-3 text-sm font-bold text-white shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-4 focus:ring-sky-200 dark:bg-white dark:text-zinc-950 dark:hover:bg-zinc-200 dark:focus:ring-sky-950" data-test="register-user-button">
|
||||
<button type="submit"
|
||||
class="w-full rounded-xl bg-zinc-950 px-4 py-3 text-sm font-bold text-white shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-4 focus:ring-sky-200 dark:bg-white dark:text-zinc-950 dark:hover:bg-zinc-200 dark:focus:ring-sky-950"
|
||||
data-test="register-user-button">
|
||||
{{ __('Create account') }}
|
||||
</button>
|
||||
</form>
|
||||
@@ -52,7 +79,8 @@
|
||||
|
||||
<div class="text-center text-sm text-zinc-600 dark:text-zinc-400">
|
||||
<span>{{ __('Already have access?') }}</span>
|
||||
<a href="{{ route('login') }}" wire:navigate class="font-semibold text-sky-700 underline underline-offset-4 hover:text-sky-900 dark:text-sky-300">{{ __('Log in') }}</a>
|
||||
<a href="{{ route('login') }}" wire:navigate
|
||||
class="font-semibold text-sky-700 underline underline-offset-4 hover:text-sky-900 dark:text-sky-300">{{ __('Log in') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,54 +1,44 @@
|
||||
<x-layouts::auth :title="__('Reset password')">
|
||||
<section class="mx-auto w-full max-w-md overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-xl shadow-zinc-900/10 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<section
|
||||
class="mx-auto w-full max-w-md overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-xl shadow-zinc-900/10 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<div class="h-1.5 bg-gradient-to-r from-emerald-500 via-sky-500 to-blue-600"></div>
|
||||
|
||||
<div class="space-y-7 p-6 sm:p-8">
|
||||
<div class="space-y-5 text-center">
|
||||
<div class="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-zinc-950 text-sm font-black text-white dark:bg-white dark:text-zinc-950">WMS</div>
|
||||
<div
|
||||
class="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-zinc-950 text-sm font-black text-white dark:bg-white dark:text-zinc-950">
|
||||
WMS</div>
|
||||
<div class="space-y-2">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-sky-700 dark:text-sky-300">{{ __('Password reset') }}</p>
|
||||
<h1 class="text-2xl font-bold tracking-tight text-zinc-950 dark:text-white">{{ __('Secure account recovery') }}</h1>
|
||||
<p class="text-sm leading-6 text-zinc-600 dark:text-zinc-400">{{ __('Create a new password for your WMS account.') }}</p>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-sky-700 dark:text-sky-300">
|
||||
{{ __('Password reset') }}</p>
|
||||
<h1 class="text-2xl font-bold tracking-tight text-zinc-950 dark:text-white">
|
||||
{{ __('Secure account recovery') }}</h1>
|
||||
<p class="text-sm leading-6 text-zinc-600 dark:text-zinc-400">
|
||||
{{ __('Create a new password for your WMS account.') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-zinc-200 bg-zinc-50 p-5 dark:border-zinc-800 dark:bg-zinc-900/60">
|
||||
<x-auth-session-status class="mb-5 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-center text-sm text-emerald-800 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-200" :status="session('status')" />
|
||||
<x-auth-session-status
|
||||
class="mb-5 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-center text-sm text-emerald-800 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-200"
|
||||
:status="session('status')" />
|
||||
|
||||
<form method="POST" action="{{ route('password.update') }}" class="flex flex-col gap-5">
|
||||
@csrf
|
||||
<input type="hidden" name="token" value="{{ request()->route('token') }}">
|
||||
|
||||
<flux:input
|
||||
name="email"
|
||||
value="{{ request('email') }}"
|
||||
:label="__('Email')"
|
||||
type="email"
|
||||
required
|
||||
autocomplete="email"
|
||||
/>
|
||||
<flux:input name="email" value="{{ request('email') }}" :label="__('Email')" type="email"
|
||||
required autocomplete="email" />
|
||||
|
||||
<flux:input
|
||||
name="password"
|
||||
:label="__('Password')"
|
||||
type="password"
|
||||
required
|
||||
autocomplete="new-password"
|
||||
:placeholder="__('Password')"
|
||||
<flux:input name="password" :label="__('Password')" type="password" required
|
||||
autocomplete="new-password" :placeholder="__('Password')"
|
||||
passwordrules="{{ \Illuminate\Validation\Rules\Password::defaults()->toPasswordRulesString() }}"
|
||||
viewable
|
||||
/>
|
||||
viewable />
|
||||
|
||||
<flux:input
|
||||
name="password_confirmation"
|
||||
:label="__('Confirm password')"
|
||||
type="password"
|
||||
required
|
||||
autocomplete="new-password"
|
||||
:placeholder="__('Confirm password')"
|
||||
<flux:input name="password_confirmation" :label="__('Confirm password')" type="password" required
|
||||
autocomplete="new-password" :placeholder="__('Confirm password')"
|
||||
passwordrules="{{ \Illuminate\Validation\Rules\Password::defaults()->toPasswordRulesString() }}"
|
||||
viewable
|
||||
/>
|
||||
viewable />
|
||||
|
||||
<flux:button type="submit" variant="primary" class="w-full" data-test="reset-password-button">
|
||||
{{ __('Reset password') }}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user