feat: dashboard working
This commit is contained in:
@@ -11,7 +11,7 @@ FlexSupport is a lightweight helpdesk and ticketing system specifically tailored
|
||||
### Backend
|
||||
- **Go 1.25** - Primary language
|
||||
- **Chi (v5)** - HTTP router and middleware
|
||||
- **Templ** - Type-safe Go templating (generates `*_templ.go` files)
|
||||
- **Templ** - Type-safe Go templating (generates `*_templ.go` files) - See the url `https://templ.guide/llms.md` for llm instructions.
|
||||
- **Shopify Integration** - Customer/order integration via `go-shopify` SDK
|
||||
|
||||
### Frontend
|
||||
|
||||
4
app.go
4
app.go
@@ -9,7 +9,6 @@ import (
|
||||
"strings"
|
||||
|
||||
cfg "flexsupport/internal/config"
|
||||
"flexsupport/internal/handlers"
|
||||
"flexsupport/internal/lib/logger"
|
||||
"flexsupport/internal/router"
|
||||
)
|
||||
@@ -33,8 +32,7 @@ func App(ctx context.Context, stdout io.Writer, getenv func(string, string) stri
|
||||
default:
|
||||
log = slog.New(slog.NewJSONHandler(stdout, logOptions))
|
||||
}
|
||||
h := handlers.NewHandler()
|
||||
r := router.NewRouter(h, log)
|
||||
r := router.NewRouter(log)
|
||||
|
||||
fmt.Println("Starting server on :8080")
|
||||
return http.ListenAndServe(":8080", r)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package handlers
|
||||
|
||||
/**
|
||||
* @Note: this is now unused and will be deleted
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
// "strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"flexsupport/internal/models"
|
||||
@@ -24,18 +25,6 @@ func NewHandler() *Handler {
|
||||
return &Handler{}
|
||||
}
|
||||
|
||||
// // Dashboard renders the main dashboard view
|
||||
// func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
|
||||
// // TODO: Fetch real data from database
|
||||
// fmt.Println("Rendering dashboard")
|
||||
// page := pages.Dashboard(getMockTickets())
|
||||
// err := layouts.BaseLayout(page).Render(r.Context(), w)
|
||||
// if err != nil {
|
||||
// http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
// // ListTickets handles the ticket listing page
|
||||
// func (h *Handler) ListTickets(w http.ResponseWriter, r *http.Request) {
|
||||
// // TODO: Implement filtering based on query parameters
|
||||
@@ -59,30 +48,6 @@ func NewHandler() *Handler {
|
||||
// }
|
||||
// }
|
||||
|
||||
func filterTicketsByStatus(tickets []models.Ticket, status string) []models.Ticket {
|
||||
result := make([]models.Ticket, 0)
|
||||
if status != "" {
|
||||
for _, ticket := range tickets {
|
||||
if ticket.Status.String() == status {
|
||||
result = append(result, ticket)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func filterTicketsBySearch(tickets []models.Ticket, search string) []models.Ticket {
|
||||
result := make([]models.Ticket, 0)
|
||||
if search != "" {
|
||||
for _, ticket := range tickets {
|
||||
if strings.Contains(ticket.CustomerName, search) || strings.Contains(ticket.IssueDescription, search) {
|
||||
result = append(result, ticket)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CreateTicket handles ticket creation
|
||||
func (h *Handler) CreateTicket(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO: Parse form data
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
// "flexsupport/ui/components/textarea"
|
||||
// "flexsupport/ui/components/toast"
|
||||
// "flexsupport/internal/middleware"
|
||||
"flexsupport/ui/modules"
|
||||
"flexsupport/ui/components/navbar"
|
||||
)
|
||||
|
||||
templ BaseLayout(contents templ.Component) {
|
||||
@@ -29,11 +29,36 @@ templ BaseLayout(contents templ.Component) {
|
||||
<script defer src="/assets/js/alpine.min.js"></script>
|
||||
@input.Script()
|
||||
@label.Script()
|
||||
<script nonce={ templ.GetNonce(ctx) }>
|
||||
(function() {
|
||||
// Get current theme preference (system, light, or dark)
|
||||
function getThemePreference() {
|
||||
return localStorage.getItem('themePreference') || 'system';
|
||||
}
|
||||
|
||||
// Apply theme based on preference
|
||||
const preference = getThemePreference();
|
||||
let isDark = false;
|
||||
|
||||
if (preference === 'system') {
|
||||
// Use system preference
|
||||
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
} else {
|
||||
// Use explicit preference
|
||||
isDark = preference === 'dark';
|
||||
}
|
||||
|
||||
// Apply theme immediately to prevent flash
|
||||
if (isDark) {
|
||||
document.documentElement.classList.add('dark');
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<title>Flex Support</title>
|
||||
</head>
|
||||
<body class="h-full" hx-ext="response-targets">
|
||||
@modules.Navbar("dickhead")
|
||||
<main class="container">
|
||||
<body class="h-full transition-colors duration-300" hx-ext="response-targets">
|
||||
@navbar.Navbar("dickhead")
|
||||
<main class="container-wrapper">
|
||||
@contents
|
||||
</main>
|
||||
</body>
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
// "flexsupport/ui/components/textarea"
|
||||
// "flexsupport/ui/components/toast"
|
||||
// "flexsupport/internal/middleware"
|
||||
"flexsupport/ui/modules"
|
||||
"flexsupport/ui/components/navbar"
|
||||
)
|
||||
|
||||
func BaseLayout(contents templ.Component) templ.Component {
|
||||
@@ -58,15 +58,28 @@ func BaseLayout(contents templ.Component) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<title>Flex Support</title></head><body class=\"h-full\" hx-ext=\"response-targets\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<script nonce=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = modules.Navbar("dickhead").Render(ctx, templ_7745c5c3_Buffer)
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(templ.GetNonce(ctx))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/layout/base.templ`, Line: 32, Col: 38}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<main class=\"container\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\">\n\t\t\t\t(function() {\n\t\t\t\t\t// Get current theme preference (system, light, or dark)\n\t\t\t\t\tfunction getThemePreference() {\n\t\t\t\t\t\treturn localStorage.getItem('themePreference') || 'system';\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// Apply theme based on preference\n\t\t\t\t\tconst preference = getThemePreference();\n\t\t\t\t\tlet isDark = false;\n\t\t\t\t\t\n\t\t\t\t\tif (preference === 'system') {\n\t\t\t\t\t\t// Use system preference\n\t\t\t\t\t\tisDark = window.matchMedia('(prefers-color-scheme: dark)').matches;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Use explicit preference\n\t\t\t\t\t\tisDark = preference === 'dark';\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// Apply theme immediately to prevent flash\n\t\t\t\t\tif (isDark) {\n\t\t\t\t\t\tdocument.documentElement.classList.add('dark');\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t</script><title>Flex Support</title></head><body class=\"h-full transition-colors duration-300\" hx-ext=\"response-targets\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = navbar.Navbar("dickhead").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<main class=\"container-wrapper\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -74,7 +87,7 @@ func BaseLayout(contents templ.Component) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</main></body></html>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</main></body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ import (
|
||||
"github.com/a-h/templ"
|
||||
)
|
||||
|
||||
func Handler(content templ.Component, options ...func(*templ.ComponentHandler)) http.Handler {
|
||||
return templ.Handler(BaseLayout(content), options...)
|
||||
func Handler(content templ.Component) http.Handler {
|
||||
return templ.Handler(BaseLayout(content))
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@ import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"flexsupport/internal/handlers"
|
||||
mw "flexsupport/internal/middleware"
|
||||
"flexsupport/static"
|
||||
|
||||
// "net/http"
|
||||
"flexsupport/internal/routes/api"
|
||||
"flexsupport/internal/routes/dashboard"
|
||||
// "flexsupport/internal/routes/tickets"
|
||||
"flexsupport/internal/routes/tickets"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
@@ -18,11 +18,10 @@ import (
|
||||
|
||||
var AppDev string = "development"
|
||||
|
||||
func NewRouter(h *handlers.Handler, log *slog.Logger) *chi.Mux {
|
||||
func NewRouter(log *slog.Logger) *chi.Mux {
|
||||
r := chi.NewMux()
|
||||
// Dashboard
|
||||
|
||||
dbHandler := dashboard.NewHandler(log)
|
||||
r.Handle("/assets/*",
|
||||
disableCacheInDevMode(
|
||||
http.StripPrefix("/assets/",
|
||||
@@ -38,28 +37,10 @@ func NewRouter(h *handlers.Handler, log *slog.Logger) *chi.Mux {
|
||||
mw.Logging(log),
|
||||
mw.TextHTMLMiddleware,
|
||||
)
|
||||
r.Get("/", dbHandler.Get)
|
||||
r.Route("/tickets", func(r chi.Router) {
|
||||
// r.Get("/", h.ListTickets)
|
||||
// r.Get("/new", h.NewTicketForm)
|
||||
r.Post("/", h.CreateTicket)
|
||||
r.Get("/search", h.SearchTickets)
|
||||
// r.Get("/{id}", h.ViewTicket)
|
||||
// r.Get("/{id}/edit", h.EditTicketForm)
|
||||
r.Post("/{id}", h.UpdateTicket)
|
||||
|
||||
// Ticket actions
|
||||
r.Post("/{id}/status", h.UpdateTicketStatus)
|
||||
r.Post("/{id}/parts", h.AddPart)
|
||||
r.Delete("/{id}/parts/{partId}", h.DeletePart)
|
||||
r.Post("/{id}/notes", h.AddNote)
|
||||
})
|
||||
})
|
||||
|
||||
// API endpoints (for htmx)
|
||||
r.Route("/api", func(r chi.Router) {
|
||||
r.Get("/stats/open", h.GetOpenTicketsCount)
|
||||
dashboard.Mount(r, dashboard.NewHandler(log, dashboard.NewService(log)))
|
||||
tickets.Mount(r, tickets.NewHandler(log, tickets.NewService(log)))
|
||||
})
|
||||
api.Mount(r, api.NewHandler(log, api.NewService(log)))
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
48
internal/routes/api/handler.go
Normal file
48
internal/routes/api/handler.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type (
|
||||
Handler interface {
|
||||
GetOpenTicketCount(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
handler struct {
|
||||
log *slog.Logger
|
||||
service Service
|
||||
}
|
||||
)
|
||||
|
||||
func NewHandler(log *slog.Logger, svc Service) Handler {
|
||||
return &handler{
|
||||
log: log.With("Handler", "api"),
|
||||
service: svc,
|
||||
}
|
||||
}
|
||||
|
||||
func Mount(r chi.Router, h Handler) {
|
||||
r.Route("/api", func(r chi.Router) {
|
||||
r.Route("/stats", func(r chi.Router) {
|
||||
r.Get("/open", h.GetOpenTicketCount)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (h *handler) GetOpenTicketCount(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO: Query database
|
||||
count, err := h.service.OpenTicketsCount(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
countString := fmt.Sprintf("%d", count)
|
||||
|
||||
fmt.Fprintf(w, "%s", countString)
|
||||
}
|
||||
25
internal/routes/api/service.go
Normal file
25
internal/routes/api/service.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
type (
|
||||
Service interface {
|
||||
OpenTicketsCount(ctx context.Context) (int, error)
|
||||
}
|
||||
service struct {
|
||||
log *slog.Logger
|
||||
}
|
||||
)
|
||||
|
||||
func NewService(log *slog.Logger) Service {
|
||||
return &service{
|
||||
log: log.With("Service", "api"),
|
||||
}
|
||||
}
|
||||
|
||||
func (s service) OpenTicketsCount(ctx context.Context) (int, error) {
|
||||
return 2, nil
|
||||
}
|
||||
@@ -2,17 +2,17 @@ package dashboard
|
||||
|
||||
import (
|
||||
"flexsupport/internal/models"
|
||||
"flexsupport/internal/utils"
|
||||
"flexsupport/ui/components/card"
|
||||
"fmt"
|
||||
"flexsupport/ui/partials/tables"
|
||||
"flexsupport/ui/partials/search"
|
||||
)
|
||||
|
||||
templ Dashboard(tickets []models.Ticket) {
|
||||
<div class="px-4 py-6 sm:px-0">
|
||||
<div class="container px-4 py-6 sm:px-0">
|
||||
<!-- Page Header -->
|
||||
<div class="mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-900">Ticket Dashboard</h2>
|
||||
<p class="mt-1 text-sm text-gray-600">Manage and track repair tickets</p>
|
||||
<h2 class="text-2xl font-bold">Ticket Dashboard</h2>
|
||||
<p class="mt-1 text-sm ">Manage and track repair tickets</p>
|
||||
</div>
|
||||
<!-- Stats Cards -->
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4 mb-8">
|
||||
@@ -33,9 +33,9 @@ templ Dashboard(tickets []models.Ticket) {
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 truncate">Open Tickets</dt>
|
||||
<dt class="text-sm font-medium truncate">Open Tickets</dt>
|
||||
<dd
|
||||
class="text-2xl font-semibold text-gray-900"
|
||||
class="text-2xl font-semibold "
|
||||
hx-get="/api/stats/open"
|
||||
hx-trigger="load, every 30s"
|
||||
hx-target="#open-tickets"
|
||||
@@ -52,7 +52,7 @@ templ Dashboard(tickets []models.Ticket) {
|
||||
<div class="flex items-center">
|
||||
<div class="shrink-0">
|
||||
<div class="rounded-md bg-yellow-500 p-3">
|
||||
<svg class="h-6 w-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="h-6 w-6 text-primary-foreground" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
@@ -64,9 +64,9 @@ templ Dashboard(tickets []models.Ticket) {
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 truncate">In Progress</dt>
|
||||
<dt class="text-sm font-medium truncate">In Progress</dt>
|
||||
<dd
|
||||
class="text-2xl font-semibold text-gray-900"
|
||||
class="text-2xl font-semibold "
|
||||
hx-get="/api/stats/inprogress"
|
||||
hx-trigger="load, every 30s"
|
||||
hx-target="#inprogress-tickets"
|
||||
@@ -95,9 +95,9 @@ templ Dashboard(tickets []models.Ticket) {
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 truncate">Overdue</dt>
|
||||
<dt class="text-sm font-medium truncate">Overdue</dt>
|
||||
<dd
|
||||
class="text-2xl font-semibold text-gray-900"
|
||||
class="text-2xl font-semibold "
|
||||
hx-get="/api/stats/overdue"
|
||||
hx-trigger="load, every 30s"
|
||||
hx-target="#overdue-tickets"
|
||||
@@ -140,135 +140,16 @@ templ Dashboard(tickets []models.Ticket) {
|
||||
@card.Card() {
|
||||
@card.Content() {
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<span class="htmx-indicator">
|
||||
<img src="/assets/icons/loader.svg" class="size-4 animate-spin" alt="Loading..."/> Loading...
|
||||
</span>
|
||||
<div class="flex-1">
|
||||
<input
|
||||
type="search"
|
||||
name="search"
|
||||
placeholder="Search tickets..."
|
||||
class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
hx-get="/"
|
||||
hx-trigger="keyup changed delay:300ms"
|
||||
hx-target="#ticket-list"
|
||||
hx-indicator=".htmx-indicator"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<select
|
||||
name="status"
|
||||
class="block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md"
|
||||
hx-get="/"
|
||||
hx-trigger="change"
|
||||
hx-target="#ticket-list"
|
||||
hx-swap="innerHTML"
|
||||
hx-include="[name='search']"
|
||||
>
|
||||
<option value="">All Statuses</option>
|
||||
<option value="new">New</option>
|
||||
<option value="in_progress">In Progress</option>
|
||||
<option value="waiting_parts">Waiting for Parts</option>
|
||||
<option value="ready">Ready for Pickup</option>
|
||||
<option value="completed">Completed</option>
|
||||
</select>
|
||||
<a
|
||||
href="/tickets/new"
|
||||
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
>
|
||||
New Ticket
|
||||
</a>
|
||||
</div>
|
||||
@search.SearchTickets("", "")
|
||||
<a
|
||||
href="/tickets/new"
|
||||
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
>
|
||||
New Ticket
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@TicketsTable(tickets)
|
||||
@tables.TicketsTable(tickets)
|
||||
</div>
|
||||
}
|
||||
|
||||
templ TicketsTable(tickets []models.Ticket) {
|
||||
<div
|
||||
class="bg-white shadow rounded-lg overflow-hidden"
|
||||
>
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Ticket #
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Customer
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Item
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Status
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Assigned To
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Due Date
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<!-- <tbody id="ticket-list" hx-get="/tickets" hx-trigger="load, every 30s" hx-select="#ticket-list" hx-swap="innerHTML" class="bg-white divide-y divide-gray-200"> -->
|
||||
@templ.Fragment("ticket-list") {
|
||||
<tbody
|
||||
class="bg-white divide-y divide-gray-200"
|
||||
id="ticket-list"
|
||||
hx-get="/?status={{ .status }}&search={{ .search }}&template=ticket-list"
|
||||
hx-trigger="load, every 30s"
|
||||
hx-target="#ticket-list"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
for _, ticket := range tickets {
|
||||
{{ ticketUrl := fmt.Sprintf("/tickets/%d", ticket.ID) }}
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
<a href={ ticketUrl } class="text-blue-600 hover:text-blue-900">{ ticket.ID }</a>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="text-sm font-medium text-gray-900">{ ticket.CustomerName }</div>
|
||||
<div class="text-sm text-gray-500">{ ticket.CustomerEmail }</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{ ticket.ItemType }
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<span
|
||||
class={ utils.TwMerge("px-2 inline-flex text-xs leading-5 font-semibold rounded-full", ticket.StatusClass()) }
|
||||
>
|
||||
{ ticket.StatusDisplay() }
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
if ticket.AssignedTo != "" {
|
||||
{ ticket.AssignedTo }
|
||||
} else {
|
||||
<span class="text-gray-400">Unassigned</span>
|
||||
}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{ ticket.DueDate.Format("2006-01-02") }
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<a href={ fmt.Sprintf("/tickets/%d/edit", ticket.ID) } class="text-blue-600 hover:text-blue-900">Edit</a>
|
||||
<a href={ ticketUrl } class="text-gray-600 hover:text-gray-900">View</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
}
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
// templ TicketRows(tickets []models.Ticket) {
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -10,9 +10,9 @@ import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"flexsupport/internal/models"
|
||||
"flexsupport/internal/utils"
|
||||
"flexsupport/ui/components/card"
|
||||
"fmt"
|
||||
"flexsupport/ui/partials/search"
|
||||
"flexsupport/ui/partials/tables"
|
||||
)
|
||||
|
||||
func Dashboard(tickets []models.Ticket) templ.Component {
|
||||
@@ -36,7 +36,7 @@ func Dashboard(tickets []models.Ticket) templ.Component {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"px-4 py-6 sm:px-0\"><!-- Page Header --><div class=\"mb-6\"><h2 class=\"text-2xl font-bold text-gray-900\">Ticket Dashboard</h2><p class=\"mt-1 text-sm text-gray-600\">Manage and track repair tickets</p></div><!-- Stats Cards --><div class=\"grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4 mb-8\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"container px-4 py-6 sm:px-0\"><!-- Page Header --><div class=\"mb-6\"><h2 class=\"text-2xl font-bold\">Ticket Dashboard</h2><p class=\"mt-1 text-sm \">Manage and track repair tickets</p></div><!-- Stats Cards --><div class=\"grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4 mb-8\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -64,7 +64,7 @@ func Dashboard(tickets []models.Ticket) templ.Component {
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"flex items-center\"><div class=\"flex-shrink-0\"><div class=\"rounded-md bg-blue-500 p-3\"><svg class=\"h-6 w-6 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"></path></svg></div></div><div class=\"ml-5 w-0 flex-1\"><dl><dt class=\"text-sm font-medium text-gray-500 truncate\">Open Tickets</dt><dd class=\"text-2xl font-semibold text-gray-900\" hx-get=\"/api/stats/open\" hx-trigger=\"load, every 30s\" hx-target=\"#open-tickets\"><div id=\"open-tickets\" readonly></div></dd></dl></div></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"flex items-center\"><div class=\"flex-shrink-0\"><div class=\"rounded-md bg-blue-500 p-3\"><svg class=\"h-6 w-6 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"></path></svg></div></div><div class=\"ml-5 w-0 flex-1\"><dl><dt class=\"text-sm font-medium truncate\">Open Tickets</dt><dd class=\"text-2xl font-semibold \" hx-get=\"/api/stats/open\" hx-trigger=\"load, every 30s\" hx-target=\"#open-tickets\"><div id=\"open-tickets\" readonly></div></dd></dl></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -104,7 +104,7 @@ func Dashboard(tickets []models.Ticket) templ.Component {
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<div class=\"flex items-center\"><div class=\"shrink-0\"><div class=\"rounded-md bg-yellow-500 p-3\"><svg class=\"h-6 w-6 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\"></path></svg></div></div><div class=\"ml-5 w-0 flex-1\"><dl><dt class=\"text-sm font-medium text-gray-500 truncate\">In Progress</dt><dd class=\"text-2xl font-semibold text-gray-900\" hx-get=\"/api/stats/inprogress\" hx-trigger=\"load, every 30s\" hx-target=\"#inprogress-tickets\"><div id=\"inprogress-tickets\" readonly></div></dd></dl></div></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<div class=\"flex items-center\"><div class=\"shrink-0\"><div class=\"rounded-md bg-yellow-500 p-3\"><svg class=\"h-6 w-6 text-primary-foreground\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\"></path></svg></div></div><div class=\"ml-5 w-0 flex-1\"><dl><dt class=\"text-sm font-medium truncate\">In Progress</dt><dd class=\"text-2xl font-semibold \" hx-get=\"/api/stats/inprogress\" hx-trigger=\"load, every 30s\" hx-target=\"#inprogress-tickets\"><div id=\"inprogress-tickets\" readonly></div></dd></dl></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -144,7 +144,7 @@ func Dashboard(tickets []models.Ticket) templ.Component {
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"flex items-center\"><div class=\"flex-shrink-0\"><div class=\"rounded-md bg-red-500 p-3\"><svg class=\"h-6 w-6 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"></path></svg></div></div><div class=\"ml-5 w-0 flex-1\"><dl><dt class=\"text-sm font-medium text-gray-500 truncate\">Overdue</dt><dd class=\"text-2xl font-semibold text-gray-900\" hx-get=\"/api/stats/overdue\" hx-trigger=\"load, every 30s\" hx-target=\"#overdue-tickets\"><div id=\"overdue-tickets\" readonly></div></dd></dl></div></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"flex items-center\"><div class=\"flex-shrink-0\"><div class=\"rounded-md bg-red-500 p-3\"><svg class=\"h-6 w-6 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"></path></svg></div></div><div class=\"ml-5 w-0 flex-1\"><dl><dt class=\"text-sm font-medium truncate\">Overdue</dt><dd class=\"text-2xl font-semibold \" hx-get=\"/api/stats/overdue\" hx-trigger=\"load, every 30s\" hx-target=\"#overdue-tickets\"><div id=\"overdue-tickets\" readonly></div></dd></dl></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -228,7 +228,15 @@ func Dashboard(tickets []models.Ticket) templ.Component {
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\"><span class=\"htmx-indicator\"><img src=\"/assets/icons/loader.svg\" class=\"size-4 animate-spin\" alt=\"Loading...\"> Loading...</span><div class=\"flex-1\"><input type=\"search\" name=\"search\" placeholder=\"Search tickets...\" class=\"shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md\" hx-get=\"/\" hx-trigger=\"keyup changed delay:300ms\" hx-target=\"#ticket-list\" hx-indicator=\".htmx-indicator\"></div><div class=\"flex gap-2\"><select name=\"status\" class=\"block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md\" hx-get=\"/\" hx-trigger=\"change\" hx-target=\"#ticket-list\" hx-swap=\"innerHTML\" hx-include=\"[name='search']\"><option value=\"\">All Statuses</option> <option value=\"new\">New</option> <option value=\"in_progress\">In Progress</option> <option value=\"waiting_parts\">Waiting for Parts</option> <option value=\"ready\">Ready for Pickup</option> <option value=\"completed\">Completed</option></select> <a href=\"/tickets/new\" class=\"inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\">New Ticket</a></div></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = search.SearchTickets("", "").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<a href=\"/tickets/new\" class=\"inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\">New Ticket</a></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -244,11 +252,11 @@ func Dashboard(tickets []models.Ticket) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = TicketsTable(tickets).Render(ctx, templ_7745c5c3_Buffer)
|
||||
templ_7745c5c3_Err = tables.TicketsTable(tickets).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -256,233 +264,4 @@ func Dashboard(tickets []models.Ticket) templ.Component {
|
||||
})
|
||||
}
|
||||
|
||||
func TicketsTable(tickets []models.Ticket) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var12 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var12 == nil {
|
||||
templ_7745c5c3_Var12 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<div class=\"bg-white shadow rounded-lg overflow-hidden\"><table class=\"min-w-full divide-y divide-gray-200\"><thead class=\"bg-gray-50\"><tr><th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Ticket #</th><th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Customer</th><th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Item</th><th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Status</th><th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Assigned To</th><th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Due Date</th><th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Actions</th></tr></thead><!-- <tbody id=\"ticket-list\" hx-get=\"/tickets\" hx-trigger=\"load, every 30s\" hx-select=\"#ticket-list\" hx-swap=\"innerHTML\" class=\"bg-white divide-y divide-gray-200\"> -->")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var13 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<tbody class=\"bg-white divide-y divide-gray-200\" id=\"ticket-list\" hx-get=\"/?status={{ .status }}&search={{ .search }}&template=ticket-list\" hx-trigger=\"load, every 30s\" hx-target=\"#ticket-list\" hx-swap=\"outerHTML\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, ticket := range tickets {
|
||||
ticketUrl := fmt.Sprintf("/tickets/%d", ticket.ID)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<tr class=\"hover:bg-gray-50\"><td class=\"px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900\"><a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var14 templ.SafeURL
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinURLErrs(ticketUrl)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/routes/dashboard/dashboard.templ`, Line: 233, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\" class=\"text-blue-600 hover:text-blue-900\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/routes/dashboard/dashboard.templ`, Line: 233, Col: 83}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</a></td><td class=\"px-6 py-4 whitespace-nowrap\"><div class=\"text-sm font-medium text-gray-900\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var16 string
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.CustomerName)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/routes/dashboard/dashboard.templ`, Line: 236, Col: 76}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div><div class=\"text-sm text-gray-500\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var17 string
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.CustomerEmail)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/routes/dashboard/dashboard.templ`, Line: 237, Col: 65}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</div></td><td class=\"px-6 py-4 whitespace-nowrap\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var18 string
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.ItemType)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/routes/dashboard/dashboard.templ`, Line: 240, Col: 25}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</td><td class=\"px-6 py-4 whitespace-nowrap\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var19 = []any{utils.TwMerge("px-2 inline-flex text-xs leading-5 font-semibold rounded-full", ticket.StatusClass())}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var19...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<span class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var20 string
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var19).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/routes/dashboard/dashboard.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var21 string
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.StatusDisplay())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/routes/dashboard/dashboard.templ`, Line: 246, Col: 33}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</span></td><td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if ticket.AssignedTo != "" {
|
||||
var templ_7745c5c3_Var22 string
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.AssignedTo)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/routes/dashboard/dashboard.templ`, Line: 251, Col: 28}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<span class=\"text-gray-400\">Unassigned</span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</td><td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var23 string
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.DueDate.Format("2006-01-02"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/routes/dashboard/dashboard.templ`, Line: 257, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</td><td class=\"px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2\"><a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var24 templ.SafeURL
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinURLErrs(fmt.Sprintf("/tickets/%d/edit", ticket.ID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/routes/dashboard/dashboard.templ`, Line: 260, Col: 60}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" class=\"text-blue-600 hover:text-blue-900\">Edit</a> <a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var25 templ.SafeURL
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinURLErrs(ticketUrl)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/routes/dashboard/dashboard.templ`, Line: 261, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\" class=\"text-gray-600 hover:text-gray-900\">View</a></td></tr>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</tbody>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = templ.Fragment("ticket-list").Render(templ.WithChildren(ctx, templ_7745c5c3_Var13), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</table></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// templ TicketRows(tickets []models.Ticket) {
|
||||
|
||||
// }
|
||||
// }
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
|
||||
@@ -4,85 +4,45 @@ import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"flexsupport/internal/layout"
|
||||
"flexsupport/internal/models"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
log *slog.Logger
|
||||
}
|
||||
type (
|
||||
Handler interface {
|
||||
Get(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
handler struct {
|
||||
log *slog.Logger
|
||||
service Service
|
||||
}
|
||||
)
|
||||
|
||||
func NewHandler(log *slog.Logger) *Handler {
|
||||
return &Handler{
|
||||
log: log,
|
||||
func NewHandler(log *slog.Logger, svc Service) Handler {
|
||||
return &handler{
|
||||
log: log,
|
||||
service: svc,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
h.Get(w, r)
|
||||
default:
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
func Mount(r chi.Router, h Handler) {
|
||||
r.Get("/", h.Get)
|
||||
}
|
||||
|
||||
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
status := r.URL.Query().Get("status")
|
||||
search := r.URL.Query().Get("search")
|
||||
template := r.URL.Query().Get("template")
|
||||
|
||||
// TODO: Fetch real data from database
|
||||
tickets := getMockTickets()
|
||||
|
||||
if status != "" {
|
||||
tickets = filterTicketsByStatus(tickets, status)
|
||||
}
|
||||
if search != "" {
|
||||
tickets = filterTicketsBySearch(tickets, search)
|
||||
func (h handler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
tickets, err := h.service.List(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var opts []func(*templ.ComponentHandler)
|
||||
if template != "" {
|
||||
opts = append(opts, templ.WithFragments(template))
|
||||
}
|
||||
v := layout.Handler(Dashboard(tickets), opts...)
|
||||
v := layout.Handler(Dashboard(tickets))
|
||||
v.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func getMockTickets() []models.Ticket {
|
||||
return []models.Ticket{
|
||||
{
|
||||
ID: 1001,
|
||||
Status: "new",
|
||||
Priority: "high",
|
||||
CustomerName: "John Doe",
|
||||
CustomerPhone: "(555) 123-4567",
|
||||
ItemType: models.Bag,
|
||||
ItemModel: "Backpack",
|
||||
IssueDescription: "Broken strap, needs replacement",
|
||||
AssignedTo: "Mike Tech",
|
||||
DueDate: time.Now().Add(48 * time.Hour),
|
||||
},
|
||||
{
|
||||
ID: 1002,
|
||||
Status: "in_progress",
|
||||
Priority: "normal",
|
||||
CustomerName: "Jane Smith",
|
||||
CustomerPhone: "(555) 987-6543",
|
||||
ItemType: models.Boot,
|
||||
ItemModel: "Keen",
|
||||
IssueDescription: "Sole needs replaced",
|
||||
AssignedTo: "Sarah Tech",
|
||||
DueDate: time.Now().Add(72 * time.Hour),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func filterTicketsByStatus(tickets []models.Ticket, status string) []models.Ticket {
|
||||
result := make([]models.Ticket, 0)
|
||||
if status != "" {
|
||||
|
||||
58
internal/routes/dashboard/service.go
Normal file
58
internal/routes/dashboard/service.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"flexsupport/internal/models"
|
||||
)
|
||||
|
||||
type (
|
||||
Service interface {
|
||||
List(ctx context.Context) ([]models.Ticket, error)
|
||||
}
|
||||
|
||||
service struct {
|
||||
log *slog.Logger
|
||||
}
|
||||
)
|
||||
|
||||
func NewService(log *slog.Logger) Service {
|
||||
return &service{
|
||||
log: log.With("Dashboard Service"),
|
||||
}
|
||||
}
|
||||
|
||||
func (s service) List(context.Context) ([]models.Ticket, error) {
|
||||
return getMockTickets(), nil
|
||||
}
|
||||
|
||||
func getMockTickets() []models.Ticket {
|
||||
return []models.Ticket{
|
||||
{
|
||||
ID: 1001,
|
||||
Status: "new",
|
||||
Priority: "high",
|
||||
CustomerName: "John Doe",
|
||||
CustomerPhone: "(555) 123-4567",
|
||||
ItemType: models.Bag,
|
||||
ItemModel: "Backpack",
|
||||
IssueDescription: "Broken strap, needs replacement",
|
||||
AssignedTo: "Mike Tech",
|
||||
DueDate: time.Now().Add(48 * time.Hour),
|
||||
},
|
||||
{
|
||||
ID: 1002,
|
||||
Status: "in_progress",
|
||||
Priority: "normal",
|
||||
CustomerName: "Jane Smith",
|
||||
CustomerPhone: "(555) 987-6543",
|
||||
ItemType: models.Boot,
|
||||
ItemModel: "Keen",
|
||||
IssueDescription: "Sole needs replaced",
|
||||
AssignedTo: "Sarah Tech",
|
||||
DueDate: time.Now().Add(72 * time.Hour),
|
||||
},
|
||||
}
|
||||
}
|
||||
106
internal/routes/tickets/handler.go
Normal file
106
internal/routes/tickets/handler.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package tickets
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"flexsupport/internal/layout"
|
||||
"flexsupport/internal/models"
|
||||
"flexsupport/ui/partials/rows"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type (
|
||||
Handler interface {
|
||||
Search(w http.ResponseWriter, r *http.Request)
|
||||
Get(w http.ResponseWriter, r *http.Request)
|
||||
New(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
handler struct {
|
||||
log *slog.Logger
|
||||
service Service
|
||||
}
|
||||
)
|
||||
|
||||
func NewHandler(log *slog.Logger, svc Service) Handler {
|
||||
return &handler{
|
||||
log: log.With("Tickets Handler"),
|
||||
service: svc,
|
||||
}
|
||||
}
|
||||
|
||||
func Mount(r chi.Router, h Handler) {
|
||||
r.Route("/tickets", func(r chi.Router) {
|
||||
r.Get("/", h.Search)
|
||||
r.Route("/{ticketId}", func(r chi.Router) {
|
||||
r.Get("/", h.Get)
|
||||
})
|
||||
r.Get("/new", h.New)
|
||||
})
|
||||
}
|
||||
|
||||
func (h handler) Search(w http.ResponseWriter, r *http.Request) {
|
||||
status := r.URL.Query().Get("status")
|
||||
search := r.URL.Query().Get("search")
|
||||
tickets, err := h.service.Search(r.Context(), search, status)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
switch isHTMX(r) {
|
||||
case true:
|
||||
err = rows.TicketRows(tickets).Render(r.Context(), w)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = layout.BaseLayout(TicketsPage(tickets, search, status)).Render(r.Context(), w)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h handler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "ticketId")
|
||||
ticketID, err := strconv.ParseInt(id, 10, 64)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid ticket ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
ticket, err := h.service.Get(r.Context(), ticketID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
page := TicketPage(ticket)
|
||||
err = layout.BaseLayout(page).Render(r.Context(), w)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h handler) New(w http.ResponseWriter, r *http.Request) {
|
||||
page := TicketForm(models.Ticket{})
|
||||
err := layout.BaseLayout(page).Render(r.Context(), w)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func isHTMX(r *http.Request) bool {
|
||||
// Check for "HX-Request" header
|
||||
if r.Header.Get("HX-Request") != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
123
internal/routes/tickets/service.go
Normal file
123
internal/routes/tickets/service.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package tickets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"flexsupport/internal/models"
|
||||
)
|
||||
|
||||
type (
|
||||
Service interface {
|
||||
Search(ctx context.Context, search, status string) ([]models.Ticket, error)
|
||||
Get(ctx context.Context, id int64) (models.Ticket, error)
|
||||
}
|
||||
|
||||
service struct {
|
||||
log *slog.Logger
|
||||
}
|
||||
)
|
||||
|
||||
func NewService(log *slog.Logger) Service {
|
||||
return &service{
|
||||
log: log.With("Service", "Tickets"),
|
||||
}
|
||||
}
|
||||
|
||||
func (s service) Search(ctx context.Context, search, status string) ([]models.Ticket, error) {
|
||||
s.log.Debug("Searching for tickets", "search", search, "status", status)
|
||||
tickets := getMockTickets()
|
||||
if status != "" {
|
||||
tickets = filterTicketsByStatus(tickets, status)
|
||||
}
|
||||
if search != "" {
|
||||
tickets = filterTicketsBySearch(tickets, search)
|
||||
}
|
||||
return tickets, nil
|
||||
}
|
||||
|
||||
func (s service) Get(ctx context.Context, id int64) (models.Ticket, error) {
|
||||
tickets := getMockTickets()
|
||||
for _, ticket := range tickets {
|
||||
if ticket.ID == id {
|
||||
return ticket, nil
|
||||
}
|
||||
}
|
||||
return models.Ticket{}, errors.New("ticket not found")
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary mock data and filtering
|
||||
* TODO: Fetch real data from database
|
||||
*/
|
||||
|
||||
func getMockTickets() []models.Ticket {
|
||||
return []models.Ticket{
|
||||
{
|
||||
ID: 1001,
|
||||
Status: "new",
|
||||
Priority: "high",
|
||||
CustomerName: "John Doe",
|
||||
CustomerPhone: "(555) 123-4567",
|
||||
ItemType: models.Bag,
|
||||
ItemModel: "Backpack",
|
||||
IssueDescription: "Broken strap, needs replacement",
|
||||
AssignedTo: "Mike Tech",
|
||||
DueDate: time.Now().Add(48 * time.Hour),
|
||||
},
|
||||
{
|
||||
ID: 1002,
|
||||
Status: "in_progress",
|
||||
Priority: "normal",
|
||||
CustomerName: "Jane Smith",
|
||||
CustomerPhone: "(555) 987-6543",
|
||||
ItemType: models.Boot,
|
||||
ItemModel: "Keen",
|
||||
IssueDescription: "Sole needs replaced",
|
||||
AssignedTo: "Sarah Tech",
|
||||
DueDate: time.Now().Add(72 * time.Hour),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getMockTicket(id int64) models.Ticket {
|
||||
return models.Ticket{
|
||||
ID: id,
|
||||
Status: "in_progress",
|
||||
Priority: "high",
|
||||
CustomerName: "John Doe",
|
||||
CustomerPhone: "(555) 123-4567",
|
||||
ItemType: models.Bag,
|
||||
ItemModel: "Backpack",
|
||||
IssueDescription: "Broken strap, needs replacement",
|
||||
AssignedTo: "Mike Tech",
|
||||
DueDate: time.Now().Add(48 * time.Hour),
|
||||
}
|
||||
}
|
||||
|
||||
func filterTicketsByStatus(tickets []models.Ticket, status string) []models.Ticket {
|
||||
result := make([]models.Ticket, 0)
|
||||
if status != "" {
|
||||
for _, ticket := range tickets {
|
||||
if ticket.Status.String() == status {
|
||||
result = append(result, ticket)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func filterTicketsBySearch(tickets []models.Ticket, search string) []models.Ticket {
|
||||
result := make([]models.Ticket, 0)
|
||||
if search != "" {
|
||||
for _, ticket := range tickets {
|
||||
if strings.Contains(ticket.CustomerName, search) || strings.Contains(ticket.IssueDescription, search) {
|
||||
result = append(result, ticket)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package tickets
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"flexsupport/internal/layout"
|
||||
"flexsupport/internal/models"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
func (h *Handler) NewTicketForm(w http.ResponseWriter, r *http.Request) {
|
||||
page := TicketForm(models.Ticket{})
|
||||
err := layout.BaseLayout(page).Render(r.Context(), w)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
155
internal/routes/tickets/tickets.templ
Normal file
155
internal/routes/tickets/tickets.templ
Normal file
@@ -0,0 +1,155 @@
|
||||
package tickets
|
||||
|
||||
import (
|
||||
"flexsupport/internal/models"
|
||||
"flexsupport/ui/partials/tables"
|
||||
"flexsupport/ui/partials/search"
|
||||
"flexsupport/ui/components/card"
|
||||
)
|
||||
|
||||
templ TicketsPage(tickets []models.Ticket, term, status string) {
|
||||
<div class="px-4 py-6 sm:px-0">
|
||||
<!-- Page Header -->
|
||||
<div class="mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-900">Ticket Dashboard</h2>
|
||||
<p class="mt-1 text-sm text-gray-600">Manage and track repair tickets</p>
|
||||
</div>
|
||||
<!-- Stats Cards -->
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4 mb-8">
|
||||
@card.Card() {
|
||||
@card.Content() {
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="rounded-md bg-blue-500 p-3">
|
||||
<svg class="h-6 w-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 truncate">Open Tickets</dt>
|
||||
<dd
|
||||
class="text-2xl font-semibold text-gray-900"
|
||||
hx-get="/api/stats/open"
|
||||
hx-trigger="load, every 30s"
|
||||
hx-target="#open-tickets"
|
||||
>
|
||||
<div id="open-tickets" readonly></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@card.Card() {
|
||||
@card.Content() {
|
||||
<div class="flex items-center">
|
||||
<div class="shrink-0">
|
||||
<div class="rounded-md bg-yellow-500 p-3">
|
||||
<svg class="h-6 w-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 truncate">In Progress</dt>
|
||||
<dd
|
||||
class="text-2xl font-semibold text-gray-900"
|
||||
hx-get="/api/stats/inprogress"
|
||||
hx-trigger="load, every 30s"
|
||||
hx-target="#inprogress-tickets"
|
||||
>
|
||||
<div id="inprogress-tickets" readonly></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@card.Card() {
|
||||
@card.Content() {
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="rounded-md bg-red-500 p-3">
|
||||
<svg class="h-6 w-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 truncate">Overdue</dt>
|
||||
<dd
|
||||
class="text-2xl font-semibold text-gray-900"
|
||||
hx-get="/api/stats/overdue"
|
||||
hx-trigger="load, every 30s"
|
||||
hx-target="#overdue-tickets"
|
||||
>
|
||||
<div id="overdue-tickets" readonly></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@card.Card() {
|
||||
@card.Content() {
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="rounded-md bg-green-500 p-3">
|
||||
<svg class="h-6 w-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 truncate">Completed Today</dt>
|
||||
<dd
|
||||
class="text-2xl font-semibold text-gray-900"
|
||||
hx-get="/api/stats/completed"
|
||||
hx-trigger="load, every 30s"
|
||||
hx-target="#completed-today"
|
||||
>
|
||||
<div id="completed-today" readonly></div>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<!-- Filters and Search -->
|
||||
@card.Card() {
|
||||
@card.Content() {
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
@search.SearchTickets(term, status)
|
||||
<a
|
||||
href="/tickets/new"
|
||||
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
>
|
||||
New Ticket
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@tables.TicketsTable(tickets)
|
||||
</div>
|
||||
}
|
||||
267
internal/routes/tickets/tickets_templ.go
Normal file
267
internal/routes/tickets/tickets_templ.go
Normal file
@@ -0,0 +1,267 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.960
|
||||
package tickets
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"flexsupport/internal/models"
|
||||
"flexsupport/ui/components/card"
|
||||
"flexsupport/ui/partials/search"
|
||||
"flexsupport/ui/partials/tables"
|
||||
)
|
||||
|
||||
func TicketsPage(tickets []models.Ticket, term, status string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"px-4 py-6 sm:px-0\"><!-- Page Header --><div class=\"mb-6\"><h2 class=\"text-2xl font-bold text-gray-900\">Ticket Dashboard</h2><p class=\"mt-1 text-sm text-gray-600\">Manage and track repair tickets</p></div><!-- Stats Cards --><div class=\"grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4 mb-8\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"flex items-center\"><div class=\"flex-shrink-0\"><div class=\"rounded-md bg-blue-500 p-3\"><svg class=\"h-6 w-6 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"></path></svg></div></div><div class=\"ml-5 w-0 flex-1\"><dl><dt class=\"text-sm font-medium text-gray-500 truncate\">Open Tickets</dt><dd class=\"text-2xl font-semibold text-gray-900\" hx-get=\"/api/stats/open\" hx-trigger=\"load, every 30s\" hx-target=\"#open-tickets\"><div id=\"open-tickets\" readonly></div></dd></dl></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = card.Content().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = card.Card().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var4 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var5 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<div class=\"flex items-center\"><div class=\"shrink-0\"><div class=\"rounded-md bg-yellow-500 p-3\"><svg class=\"h-6 w-6 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\"></path></svg></div></div><div class=\"ml-5 w-0 flex-1\"><dl><dt class=\"text-sm font-medium text-gray-500 truncate\">In Progress</dt><dd class=\"text-2xl font-semibold text-gray-900\" hx-get=\"/api/stats/inprogress\" hx-trigger=\"load, every 30s\" hx-target=\"#inprogress-tickets\"><div id=\"inprogress-tickets\" readonly></div></dd></dl></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = card.Content().Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = card.Card().Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var6 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var7 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"flex items-center\"><div class=\"flex-shrink-0\"><div class=\"rounded-md bg-red-500 p-3\"><svg class=\"h-6 w-6 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"></path></svg></div></div><div class=\"ml-5 w-0 flex-1\"><dl><dt class=\"text-sm font-medium text-gray-500 truncate\">Overdue</dt><dd class=\"text-2xl font-semibold text-gray-900\" hx-get=\"/api/stats/overdue\" hx-trigger=\"load, every 30s\" hx-target=\"#overdue-tickets\"><div id=\"overdue-tickets\" readonly></div></dd></dl></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = card.Content().Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = card.Card().Render(templ.WithChildren(ctx, templ_7745c5c3_Var6), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var8 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var9 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"flex items-center\"><div class=\"flex-shrink-0\"><div class=\"rounded-md bg-green-500 p-3\"><svg class=\"h-6 w-6 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\"></path></svg></div></div><div class=\"ml-5 w-0 flex-1\"><dl><dt class=\"text-sm font-medium text-gray-500 truncate\">Completed Today</dt><dd class=\"text-2xl font-semibold text-gray-900\" hx-get=\"/api/stats/completed\" hx-trigger=\"load, every 30s\" hx-target=\"#completed-today\"><div id=\"completed-today\" readonly></div></dd></dl></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = card.Content().Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = card.Card().Render(templ.WithChildren(ctx, templ_7745c5c3_Var8), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div><!-- Filters and Search -->")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var10 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var11 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = search.SearchTickets(term, status).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<a href=\"/tickets/new\" class=\"inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\">New Ticket</a></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = card.Content().Render(templ.WithChildren(ctx, templ_7745c5c3_Var11), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = card.Card().Render(templ.WithChildren(ctx, templ_7745c5c3_Var10), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = tables.TicketsTable(tickets).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@@ -75,7 +75,7 @@
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
/* ::selection { */
|
||||
/* @apply bg-selection text-selection-foreground; */
|
||||
@@ -85,16 +85,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Smooth transitions for htmx swaps */
|
||||
/* .htmx-swapping { */
|
||||
/* opacity: 0; */
|
||||
/* transition: opacity 200ms ease-out; */
|
||||
/* } */
|
||||
Smooth transitions for htmx swaps
|
||||
.htmx-swapping {
|
||||
opacity: 0;
|
||||
transition: opacity 200ms ease-out;
|
||||
}
|
||||
|
||||
/* Loading indicator for htmx requests */
|
||||
/* .htmx-request .htmx-indicator { */
|
||||
/* display: inline-block; */
|
||||
/* } */
|
||||
Loading indicator for htmx requests
|
||||
.htmx-request .htmx-indicator {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* .htmx-indicator { */
|
||||
/* display: none; */
|
||||
@@ -113,10 +113,41 @@
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
|
||||
.step {
|
||||
counter-increment: step;
|
||||
}
|
||||
|
||||
.step:before {
|
||||
@apply md:absolute w-8 h-8 md:w-9 md:h-9 bg-muted rounded-full font-mono font-medium text-center text-base inline-flex items-center justify-center -indent-px border-4 mr-2 border-background;
|
||||
@apply md:ml-[-50px] md:mt-[-4px];
|
||||
content: counter(step);
|
||||
}
|
||||
|
||||
.chunk-container {
|
||||
@apply shadow-none;
|
||||
}
|
||||
|
||||
.chunk-container::after {
|
||||
content: "";
|
||||
@apply absolute -inset-4 shadow-xl rounded-xl border;
|
||||
}
|
||||
.border-grid {
|
||||
@apply border-border/50 dark:border-border border-dashed;
|
||||
}
|
||||
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
/* IE and Edge */
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
}
|
||||
|
||||
.container-wrapper {
|
||||
@apply max-w-[1400px] min-[1800px]:max-w-screen-2xl min-[1400px]:border-x border-border/70 dark:border-border mx-auto w-full border-dashed;
|
||||
}
|
||||
|
||||
2
static/assets/css/output.min.css
vendored
2
static/assets/css/output.min.css
vendored
File diff suppressed because one or more lines are too long
27
ui/components/navbar/navbar.templ
Normal file
27
ui/components/navbar/navbar.templ
Normal file
@@ -0,0 +1,27 @@
|
||||
package navbar
|
||||
|
||||
templ Navbar(userName string) {
|
||||
<nav class="py-1 border-b">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between h-16">
|
||||
<div class="flex">
|
||||
<div class="shrink-0 flex items-center">
|
||||
<h1 class="text-xl font-bold text-primary">FlexSupport</h1>
|
||||
</div>
|
||||
<div class="hidden sm:ml-6 sm:flex sm:space-x-8">
|
||||
<a href="/" class="border-transparent text-primary-foreground hover:border-gray-300 hover:text-primary-foreground/60 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="/tickets/new" class="border-transparent text-primary-foreground hover:border-gray-300 hover:text-primary-foreground/60 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
|
||||
New Ticket
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="text-sm text-gray-700">{ userName }</span>
|
||||
@ThemeSwitcher()
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.960
|
||||
package modules
|
||||
package navbar
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -29,20 +29,28 @@ func Navbar(userName string) templ.Component {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<nav class=\"py-1\"><div class=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\"><div class=\"flex justify-between h-16\"><div class=\"flex\"><div class=\"shrink-0 flex items-center\"><h1 class=\"text-xl font-bold text-gray-900\">FlexSupport</h1></div><div class=\"hidden sm:ml-6 sm:flex sm:space-x-8\"><a href=\"/\" class=\"border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium\">Dashboard</a> <a href=\"/tickets\" class=\"border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium\">Tickets</a> <a href=\"/tickets/new\" class=\"border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium\">New Ticket</a> <a href=\"/technician\" class=\"border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium\">Technician View</a></div></div><div class=\"flex items-center\"><span class=\"text-sm text-gray-700\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<nav class=\"py-1 border-b\"><div class=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\"><div class=\"flex justify-between h-16\"><div class=\"flex\"><div class=\"shrink-0 flex items-center\"><h1 class=\"text-xl font-bold text-primary\">FlexSupport</h1></div><div class=\"hidden sm:ml-6 sm:flex sm:space-x-8\"><a href=\"/\" class=\"border-transparent text-primary-foreground hover:border-gray-300 hover:text-primary-foreground/60 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium\">Dashboard</a> <a href=\"/tickets/new\" class=\"border-transparent text-primary-foreground hover:border-gray-300 hover:text-primary-foreground/60 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium\">New Ticket</a></div></div><div class=\"flex items-center\"><span class=\"text-sm text-gray-700\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(userName)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/modules/navbar.templ`, Line: 27, Col: 51}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/components/navbar/navbar.templ`, Line: 21, Col: 51}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</span></div></div></div></nav>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = ThemeSwitcher().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div></div></div></nav>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
92
ui/components/navbar/themeswitcher.templ
Normal file
92
ui/components/navbar/themeswitcher.templ
Normal file
@@ -0,0 +1,92 @@
|
||||
// from https://github.com/templui/templui
|
||||
package navbar
|
||||
|
||||
import (
|
||||
"flexsupport/ui/components/button"
|
||||
"flexsupport/ui/components/icon"
|
||||
)
|
||||
|
||||
type ThemeSwitcherProps struct {
|
||||
Class string
|
||||
}
|
||||
|
||||
templ ThemeSwitcher(props ...ThemeSwitcherProps) {
|
||||
{{ var p ThemeSwitcherProps }}
|
||||
if len(props) > 0 {
|
||||
{{ p = props[0] }}
|
||||
}
|
||||
<script nonce={ templ.GetNonce(ctx) }>
|
||||
(function() {
|
||||
// Make theme functions globally available for the switcher
|
||||
window.themeUtils = window.themeUtils || {};
|
||||
|
||||
// Get current theme preference (system, light, or dark)
|
||||
window.themeUtils.getThemePreference = function() {
|
||||
return localStorage.getItem('themePreference') || 'system';
|
||||
}
|
||||
|
||||
// Apply theme based on preference
|
||||
window.themeUtils.applyTheme = function() {
|
||||
const preference = window.themeUtils.getThemePreference();
|
||||
let isDark = false;
|
||||
|
||||
if (preference === 'system') {
|
||||
// Use system preference
|
||||
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
} else {
|
||||
// Use explicit preference
|
||||
isDark = preference === 'dark';
|
||||
}
|
||||
|
||||
document.documentElement.classList.toggle('dark', isDark);
|
||||
|
||||
// Dispatch event for compatibility
|
||||
document.dispatchEvent(new CustomEvent('theme-changed'));
|
||||
}
|
||||
|
||||
// Toggle between light and dark (system only on initial state)
|
||||
window.themeUtils.cycleTheme = function() {
|
||||
const current = window.themeUtils.getThemePreference();
|
||||
let next;
|
||||
|
||||
if (current === 'system') {
|
||||
// First click from system state - determine based on current appearance
|
||||
const isDarkNow = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
next = isDarkNow ? 'light' : 'dark';
|
||||
} else {
|
||||
// Toggle between light and dark
|
||||
next = current === 'light' ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
localStorage.setItem('themePreference', next);
|
||||
window.themeUtils.applyTheme();
|
||||
}
|
||||
|
||||
// Listen for system theme changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||
if (window.themeUtils.getThemePreference() === 'system') {
|
||||
window.themeUtils.applyTheme();
|
||||
}
|
||||
});
|
||||
|
||||
// Use event delegation for click handling
|
||||
document.addEventListener('click', (e) => {
|
||||
const themeSwitcher = e.target.closest('[data-theme-switcher]');
|
||||
if (themeSwitcher) {
|
||||
e.preventDefault();
|
||||
window.themeUtils.cycleTheme();
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@button.Button(button.Props{
|
||||
Size: button.SizeIcon,
|
||||
Variant: button.VariantGhost,
|
||||
Class: p.Class,
|
||||
Attributes: templ.Attributes{
|
||||
"data-theme-switcher": "true",
|
||||
},
|
||||
}) {
|
||||
@icon.Eclipse(icon.Props{Size: 20})
|
||||
}
|
||||
}
|
||||
97
ui/components/navbar/themeswitcher_templ.go
Normal file
97
ui/components/navbar/themeswitcher_templ.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.960
|
||||
// from https://github.com/templui/templui
|
||||
|
||||
package navbar
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"flexsupport/ui/components/button"
|
||||
"flexsupport/ui/components/icon"
|
||||
)
|
||||
|
||||
type ThemeSwitcherProps struct {
|
||||
Class string
|
||||
}
|
||||
|
||||
func ThemeSwitcher(props ...ThemeSwitcherProps) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
var p ThemeSwitcherProps
|
||||
if len(props) > 0 {
|
||||
p = props[0]
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<script nonce=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(templ.GetNonce(ctx))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/components/navbar/themeswitcher.templ`, Line: 18, Col: 36}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\">\n\t\t(function() {\n\t\t\t// Make theme functions globally available for the switcher\n\t\t\twindow.themeUtils = window.themeUtils || {};\n\t\t\t\n\t\t\t// Get current theme preference (system, light, or dark)\n\t\t\twindow.themeUtils.getThemePreference = function() {\n\t\t\t\treturn localStorage.getItem('themePreference') || 'system';\n\t\t\t}\n\n\t\t\t// Apply theme based on preference\n\t\t\twindow.themeUtils.applyTheme = function() {\n\t\t\t\tconst preference = window.themeUtils.getThemePreference();\n\t\t\t\tlet isDark = false;\n\n\t\t\t\tif (preference === 'system') {\n\t\t\t\t\t// Use system preference\n\t\t\t\t\tisDark = window.matchMedia('(prefers-color-scheme: dark)').matches;\n\t\t\t\t} else {\n\t\t\t\t\t// Use explicit preference\n\t\t\t\t\tisDark = preference === 'dark';\n\t\t\t\t}\n\n\t\t\t\tdocument.documentElement.classList.toggle('dark', isDark);\n\t\t\t\t\n\t\t\t\t// Dispatch event for compatibility\n\t\t\t\tdocument.dispatchEvent(new CustomEvent('theme-changed'));\n\t\t\t}\n\n\t\t\t// Toggle between light and dark (system only on initial state)\n\t\t\twindow.themeUtils.cycleTheme = function() {\n\t\t\t\tconst current = window.themeUtils.getThemePreference();\n\t\t\t\tlet next;\n\t\t\t\t\n\t\t\t\tif (current === 'system') {\n\t\t\t\t\t// First click from system state - determine based on current appearance\n\t\t\t\t\tconst isDarkNow = window.matchMedia('(prefers-color-scheme: dark)').matches;\n\t\t\t\t\tnext = isDarkNow ? 'light' : 'dark';\n\t\t\t\t} else {\n\t\t\t\t\t// Toggle between light and dark\n\t\t\t\t\tnext = current === 'light' ? 'dark' : 'light';\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tlocalStorage.setItem('themePreference', next);\n\t\t\t\twindow.themeUtils.applyTheme();\n\t\t\t}\n\n\t\t\t// Listen for system theme changes\n\t\t\twindow.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {\n\t\t\t\tif (window.themeUtils.getThemePreference() === 'system') {\n\t\t\t\t\twindow.themeUtils.applyTheme();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Use event delegation for click handling\n\t\t\tdocument.addEventListener('click', (e) => {\n\t\t\t\tconst themeSwitcher = e.target.closest('[data-theme-switcher]');\n\t\t\t\tif (themeSwitcher) {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\twindow.themeUtils.cycleTheme();\n\t\t\t\t}\n\t\t\t});\n\t\t})();\n\t</script>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = icon.Eclipse(icon.Props{Size: 20}).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = button.Button(button.Props{
|
||||
Size: button.SizeIcon,
|
||||
Variant: button.VariantGhost,
|
||||
Class: p.Class,
|
||||
Attributes: templ.Attributes{
|
||||
"data-theme-switcher": "true",
|
||||
},
|
||||
}).Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@@ -1,32 +0,0 @@
|
||||
package modules
|
||||
|
||||
templ Navbar(userName string) {
|
||||
<nav class="py-1">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between h-16">
|
||||
<div class="flex">
|
||||
<div class="shrink-0 flex items-center">
|
||||
<h1 class="text-xl font-bold text-gray-900">FlexSupport</h1>
|
||||
</div>
|
||||
<div class="hidden sm:ml-6 sm:flex sm:space-x-8">
|
||||
<a href="/" class="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="/tickets" class="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
|
||||
Tickets
|
||||
</a>
|
||||
<a href="/tickets/new" class="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
|
||||
New Ticket
|
||||
</a>
|
||||
<a href="/technician" class="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
|
||||
Technician View
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="text-sm text-gray-700">{ userName }</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
}
|
||||
48
ui/partials/rows/ticketRows.templ
Normal file
48
ui/partials/rows/ticketRows.templ
Normal file
@@ -0,0 +1,48 @@
|
||||
package rows
|
||||
|
||||
import (
|
||||
"flexsupport/internal/models"
|
||||
"flexsupport/internal/utils"
|
||||
"fmt"
|
||||
|
||||
"flexsupport/ui/components/table"
|
||||
)
|
||||
|
||||
templ TicketRows(tickets []models.Ticket) {
|
||||
for _, ticket := range tickets {
|
||||
{{ ticketUrl := fmt.Sprintf("/tickets/%d", ticket.ID) }}
|
||||
@table.Row() {
|
||||
@table.Cell() {
|
||||
<a href={ ticketUrl } class="text-blue-600 hover:text-blue-900">{ ticket.ID }</a>
|
||||
}
|
||||
@table.Cell() {
|
||||
<div class="text-sm font-medium text-primary-foreground">{ ticket.CustomerName }</div>
|
||||
<div class="text-sm ">{ ticket.CustomerEmail }</div>
|
||||
}
|
||||
@table.Cell() {
|
||||
{ ticket.ItemType }
|
||||
}
|
||||
@table.Cell() {
|
||||
<span
|
||||
class={ utils.TwMerge("px-2 inline-flex text-xs leading-5 font-semibold rounded-full", ticket.StatusClass()) }
|
||||
>
|
||||
{ ticket.StatusDisplay() }
|
||||
</span>
|
||||
}
|
||||
@table.Cell() {
|
||||
if ticket.AssignedTo != "" {
|
||||
{ ticket.AssignedTo }
|
||||
} else {
|
||||
<span class="text-gray-400">Unassigned</span>
|
||||
}
|
||||
}
|
||||
@table.Cell() {
|
||||
{ ticket.DueDate.Format("2006-01-02") }
|
||||
}
|
||||
@table.Cell() {
|
||||
<a href={ fmt.Sprintf("/tickets/%d/edit", ticket.ID) } class="text-blue-600 hover:text-blue-900">Edit</a>
|
||||
<a href={ ticketUrl } class="text-gray-600 hover:text-gray-900">View</a>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
374
ui/partials/rows/ticketRows_templ.go
Normal file
374
ui/partials/rows/ticketRows_templ.go
Normal file
@@ -0,0 +1,374 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.960
|
||||
package rows
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"flexsupport/internal/models"
|
||||
"flexsupport/internal/utils"
|
||||
"fmt"
|
||||
|
||||
"flexsupport/ui/components/table"
|
||||
)
|
||||
|
||||
func TicketRows(tickets []models.Ticket) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
for _, ticket := range tickets {
|
||||
ticketUrl := fmt.Sprintf("/tickets/%d", ticket.ID)
|
||||
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 templ.SafeURL
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(ticketUrl)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/rows/ticketRows.templ`, Line: 16, Col: 23}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" class=\"text-blue-600 hover:text-blue-900\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/rows/ticketRows.templ`, Line: 16, Col: 79}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Cell().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var6 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"text-sm font-medium text-primary-foreground\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.CustomerName)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/rows/ticketRows.templ`, Line: 19, Col: 82}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div><div class=\"text-sm \">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.CustomerEmail)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/rows/ticketRows.templ`, Line: 20, Col: 48}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Cell().Render(templ.WithChildren(ctx, templ_7745c5c3_Var6), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var9 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
var templ_7745c5c3_Var10 string
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.ItemType)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/rows/ticketRows.templ`, Line: 23, Col: 21}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Cell().Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var11 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
var templ_7745c5c3_Var12 = []any{utils.TwMerge("px-2 inline-flex text-xs leading-5 font-semibold rounded-full", ticket.StatusClass())}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<span class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var12).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/rows/ticketRows.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.StatusDisplay())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/rows/ticketRows.templ`, Line: 29, Col: 29}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Cell().Render(templ.WithChildren(ctx, templ_7745c5c3_Var11), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var15 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
if ticket.AssignedTo != "" {
|
||||
var templ_7745c5c3_Var16 string
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.AssignedTo)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/rows/ticketRows.templ`, Line: 34, Col: 24}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<span class=\"text-gray-400\">Unassigned</span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Cell().Render(templ.WithChildren(ctx, templ_7745c5c3_Var15), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var17 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
var templ_7745c5c3_Var18 string
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(ticket.DueDate.Format("2006-01-02"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/rows/ticketRows.templ`, Line: 40, Col: 41}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Cell().Render(templ.WithChildren(ctx, templ_7745c5c3_Var17), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var19 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var20 templ.SafeURL
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinURLErrs(fmt.Sprintf("/tickets/%d/edit", ticket.ID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/rows/ticketRows.templ`, Line: 43, Col: 56}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\" class=\"text-blue-600 hover:text-blue-900\">Edit</a> <a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var21 templ.SafeURL
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinURLErrs(ticketUrl)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/rows/ticketRows.templ`, Line: 44, Col: 23}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" class=\"text-gray-600 hover:text-gray-900\">View</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Cell().Render(templ.WithChildren(ctx, templ_7745c5c3_Var19), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Row().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
45
ui/partials/search/tickets.templ
Normal file
45
ui/partials/search/tickets.templ
Normal file
@@ -0,0 +1,45 @@
|
||||
package search
|
||||
|
||||
import "flexsupport/ui/components/input"
|
||||
|
||||
templ SearchTickets(term, status string) {
|
||||
<span class="htmx-indicator">
|
||||
<img src="/assets/icons/loader.svg" class="size-4 animate-spin" alt="Loading..."/> Loading...
|
||||
</span>
|
||||
<div class="flex-1">
|
||||
@input.Input(input.Props{
|
||||
Type: input.TypeText,
|
||||
ID: "search",
|
||||
Name: "search",
|
||||
Value: term,
|
||||
Placeholder: "Search tickets...",
|
||||
Attributes: templ.Attributes{
|
||||
"hx-get": "/tickets",
|
||||
"hx-trigger": "keyup changed delay:300ms",
|
||||
"hx-target": "#ticket-list",
|
||||
"hx-indicator": ".htmx-indicator",
|
||||
"hx-replace": "innerHTML",
|
||||
},
|
||||
})
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<select
|
||||
name="status"
|
||||
id="status"
|
||||
class="block w-full pl-3 pr-10 py-2 text-base border-gray-600 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md"
|
||||
hx-get="/tickets"
|
||||
value={ status }
|
||||
hx-trigger="change"
|
||||
hx-target="#ticket-list"
|
||||
hx-swap="innerHTML"
|
||||
hx-include="#search"
|
||||
>
|
||||
<option value="">All Statuses</option>
|
||||
<option value="new" selected={ status == "new" }>New</option>
|
||||
<option value="in_progress" selected={ status == "in_progress" }>In Progress</option>
|
||||
<option value="waiting_parts" selected={ status == "waiting_parts" }>Waiting for Parts</option>
|
||||
<option value="ready" selected={ status == "ready" }>Ready for Pickup</option>
|
||||
<option value="completed" selected={ status == "completed" }>Completed</option>
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
141
ui/partials/search/tickets_templ.go
Normal file
141
ui/partials/search/tickets_templ.go
Normal file
@@ -0,0 +1,141 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.960
|
||||
package search
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "flexsupport/ui/components/input"
|
||||
|
||||
func SearchTickets(term, status string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<span class=\"htmx-indicator\"><img src=\"/assets/icons/loader.svg\" class=\"size-4 animate-spin\" alt=\"Loading...\"> Loading...</span><div class=\"flex-1\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = input.Input(input.Props{
|
||||
Type: input.TypeText,
|
||||
ID: "search",
|
||||
Name: "search",
|
||||
Value: term,
|
||||
Placeholder: "Search tickets...",
|
||||
Attributes: templ.Attributes{
|
||||
"hx-get": "/tickets",
|
||||
"hx-trigger": "keyup changed delay:300ms",
|
||||
"hx-target": "#ticket-list",
|
||||
"hx-indicator": ".htmx-indicator",
|
||||
"hx-replace": "innerHTML",
|
||||
},
|
||||
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</div><div class=\"flex gap-2\"><select name=\"status\" id=\"status\" class=\"block w-full pl-3 pr-10 py-2 text-base border-gray-600 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md\" hx-get=\"/tickets\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(status)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/search/tickets.templ`, Line: 31, Col: 17}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" hx-trigger=\"change\" hx-target=\"#ticket-list\" hx-swap=\"innerHTML\" hx-include=\"#search\"><option value=\"\">All Statuses</option> <option value=\"new\" selected=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(status == "new")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/search/tickets.templ`, Line: 38, Col: 49}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\">New</option> <option value=\"in_progress\" selected=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(status == "in_progress")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/search/tickets.templ`, Line: 39, Col: 65}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\">In Progress</option> <option value=\"waiting_parts\" selected=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(status == "waiting_parts")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/search/tickets.templ`, Line: 40, Col: 69}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\">Waiting for Parts</option> <option value=\"ready\" selected=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(status == "ready")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/search/tickets.templ`, Line: 41, Col: 53}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\">Ready for Pickup</option> <option value=\"completed\" selected=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(status == "completed")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/partials/search/tickets.templ`, Line: 42, Col: 61}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\">Completed</option></select></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
56
ui/partials/tables/ticketTable.templ
Normal file
56
ui/partials/tables/ticketTable.templ
Normal file
@@ -0,0 +1,56 @@
|
||||
package tables
|
||||
|
||||
import (
|
||||
"flexsupport/internal/models"
|
||||
"flexsupport/ui/partials/rows"
|
||||
|
||||
"flexsupport/ui/components/table"
|
||||
)
|
||||
|
||||
templ TicketsTable(tickets []models.Ticket) {
|
||||
<div
|
||||
class=" shadow rounded-lg overflow-hidden"
|
||||
hx-get="/tickets"
|
||||
hx-target="#ticket-list"
|
||||
hx-trigger="load, every 30s"
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
@table.Table(table.Props{
|
||||
Class: "min-w-full ",
|
||||
}) {
|
||||
@table.Header() {
|
||||
@table.Row() {
|
||||
@table.Head() {
|
||||
Ticket #
|
||||
}
|
||||
@table.Head() {
|
||||
Customer
|
||||
}
|
||||
@table.Head() {
|
||||
Item
|
||||
}
|
||||
@table.Head() {
|
||||
Status
|
||||
}
|
||||
@table.Head() {
|
||||
Assigned To
|
||||
}
|
||||
@table.Head() {
|
||||
Due Date
|
||||
}
|
||||
@table.Head() {
|
||||
Actions
|
||||
}
|
||||
}
|
||||
}
|
||||
@table.Body(table.BodyProps{
|
||||
ID: "ticket-list",
|
||||
}) {
|
||||
@rows.TicketRows(tickets)
|
||||
}
|
||||
if len(tickets) == 0 {
|
||||
<tr><td colspan="7">No Tickets</td></tr>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
323
ui/partials/tables/ticketTable_templ.go
Normal file
323
ui/partials/tables/ticketTable_templ.go
Normal file
@@ -0,0 +1,323 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.960
|
||||
package tables
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"flexsupport/internal/models"
|
||||
"flexsupport/ui/partials/rows"
|
||||
|
||||
"flexsupport/ui/components/table"
|
||||
)
|
||||
|
||||
func TicketsTable(tickets []models.Ticket) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\" shadow rounded-lg overflow-hidden\" hx-get=\"/tickets\" hx-target=\"#ticket-list\" hx-trigger=\"load, every 30s\" hx-swap=\"innerHTML\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var4 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var5 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "Ticket #")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Head().Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var6 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "Customer")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Head().Render(templ.WithChildren(ctx, templ_7745c5c3_Var6), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var7 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "Item")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Head().Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var8 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "Status")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Head().Render(templ.WithChildren(ctx, templ_7745c5c3_Var8), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var9 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "Assigned To")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Head().Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var10 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "Due Date")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Head().Render(templ.WithChildren(ctx, templ_7745c5c3_Var10), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var11 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "Actions")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Head().Render(templ.WithChildren(ctx, templ_7745c5c3_Var11), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Row().Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Header().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var12 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = rows.TicketRows(tickets).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Body(table.BodyProps{
|
||||
ID: "ticket-list",
|
||||
}).Render(templ.WithChildren(ctx, templ_7745c5c3_Var12), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if len(tickets) == 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<tr><td colspan=\"7\">No Tickets</td></tr>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = table.Table(table.Props{
|
||||
Class: "min-w-full ",
|
||||
}).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
Reference in New Issue
Block a user