TDesk technical documentation
React & backend integration
Reuse the markup and design. Choose which system will own routing, state, and writes.
Choose an architecture
| Approach | What to keep | What to replace |
|---|---|---|
| Static HTML + API | HTML, classes, page styles and the UI behaviors you still need. | Demo data adapters, form handlers, auth, and notifications. |
| React or another SPA | Design tokens, CSS, fonts, and HTML structure as components. | Router, data loading, event handlers, and any script that mutates framework-owned DOM. |
| Server-rendered templates | HTML layouts, fields, CSS, and small UI scripts. | Hard-coded fixtures with server-rendered data; demo submit hooks with real routes. |
Port one screen to React
- Start with the ticket list. Extract
AdminLayout,Sidebar,TicketFilters, andTicketRow. - Copy the markup into JSX. Convert
classtoclassName,fortohtmlFor, and inline style strings to objects. Keeparia-*anddata-*attributes. - Load the admin styles in their original order. Put the
assetsandvendortrees under your app’s public assets with the relative CSS font paths intact. - Own each interaction in one place. Do not load the old page script on a component whose DOM it used to render. For third-party widgets, initialize after mounting and destroy the instance during cleanup.
- Use router parameters for detail IDs. Render loading, empty, error, and success states explicitly.
export function TicketRow({ ticket, href }) {
return (
<tr>
<td><a href={href}>{ticket.subject}</a></td>
<td><span className="status status--blue">{ticket.status}</span></td>
</tr>
);
}See the official React guides for JSX and effect cleanup and external widgets.
Connect a backend in any language
Use Django, Laravel, Express, Rails, ASP.NET, or any server that can serve HTML or JSON. Extract shared HTML into a layout, render ticket rows with your template engine, and bind your real form endpoints. Keep IDs, labels, and CSS classes stable while you migrate one screen at a time.
| Suggested route | Responsibility |
|---|---|
GET /api/tickets?status=Open | Return a filtered page of tickets the current user may see. |
GET /api/tickets/:id | Return a permitted ticket and its conversation. |
POST /api/tickets | Validate and create a ticket; return its stable ID. |
POST /api/tickets/:id/replies | Validate and persist a reply or staff-only note with the proper visibility. |
PATCH /api/tickets/:id | Authorize a status, priority, or assignment change. |
Example list response:
{
"items": [{ "id": "1042", "subject": "Unable to invite new team members", "status": "Open", "priority": "High" }],
"nextCursor": null
}Example client adapter:
// A proposed same-origin endpoint. Implement it on your server first.
export async function listTickets(status, signal) {
const params = new URLSearchParams({ status });
const response = await fetch(`/api/tickets?${params}`, {
credentials: "same-origin",
headers: { Accept: "application/json" },
signal
});
if (!response.ok) throw new Error(`Ticket request failed: ${response.status}`);
return response.json();
}Use abortable reads on filter changes. Keep a draft after a failed write; disable duplicate submissions while saving and update the UI only after the server confirms. Map validation responses to their associated fields.
Keep access and data rules on the server
- Authenticate with your framework’s maintained session or identity system. Verify each ticket, reply, and attachment against the current user and workspace.
- Validate all writes. For cookie-based sessions, use HTTPS and your framework’s CSRF protections. Keep secrets and provider credentials on the server.
- Use server-side permissions for staff-only notes and admin actions. Escape text; sanitize allowed HTML before rendering rich replies. Validate upload types and sizes, and enforce access when downloading.
- Replace the dashboard sample metrics with real scoped data after ticket events are connected.