admin

Please wait...

AI Feature Suite New in v5.0.0

Luxuria ships with a complete, bring-your-own-key AI layer built on top of the template's mock-data architecture. There is no bundled backend and no Luxuria-owned AI service — instead, each admin connects their own OpenAI, Anthropic (Claude), Google Gemini, or Custom / local (Ollama, LM Studio, vLLM) API key from a dedicated settings page, and that key powers AI actions across the app.

When no key is configured, every AI surface automatically falls back to Demo Mode and shows realistic canned responses instead of an error. This means the template is fully explorable and demo-ready out of the box, while still being a real, working starting point for wiring in your own provider.

Note: All AI provider calls are made directly from the browser to the provider's API using native fetch(). There is no server-side proxy included in the template — this is by design for a frontend-only admin template, and is documented in detail in the Security & Key Storage section below.
What's included
Piece Description
AI Settings hub A single admin page (/ai-settings) to choose a provider, paste an API key, pick a model, test the connection, and save — encrypted at rest.
AI Assistant A full chat page (/ai-assistant) and a header quick-access popover panel, both sharing one conversation, for asking free-form questions about bookings, revenue, guests, or maintenance.
Flagship AI integrations Three real, working examples wired into existing pages: Bookings anomaly detection, Maintenance priority triage, and "Explain this chart" on the Revenue Report.
Reusable AI building blocks <app-ai-insight-card> and <app-ai-explain-button> shared components so you can add an AI action to any other page in a few lines.
Demo Mode Canned JSON fixtures under assets/data/ai-demo/ so every AI action works with zero configuration and zero network calls.

Supported AI Providers

Only one provider is active at a time (chosen with a select on the Settings page), but credentials for all four are stored independently so you can switch providers without re-entering keys.

Provider Endpoint called Default model Notes
Google Gemini Recommended generativelanguage.googleapis.com/v1beta/models/{model}:generateContent gemini-2.0-flash Free tier available from Google AI Studio. Supports an "Auto-Detect My Key's Models" button that queries Google for the models available on that key.
OpenAI api.openai.com/v1/chat/completions gpt-4o-mini Key from platform.openai.com.
Anthropic (Claude) api.anthropic.com/v1/messages claude-3-5-sonnet-latest Key from console.anthropic.com. Uses Anthropic's direct-browser-access header since there is no backend proxy.
Custom / Local {baseUrl}/chat/completions (OpenAI-compatible) llama3 Point it at Ollama, LM Studio, vLLM, or any OpenAI-compatible reverse proxy. API key is optional for keyless local endpoints.

AI Settings Page

Route: /ai-settings · Menu icon: smart_toy · Access: Admin only (API keys are sensitive/billable, so this page is gated the same way as Security and Hotel Settings).

How to configure a provider
  1. Open AI & LLM Configuration from the sidebar (under Hotel Settings / Security).
  2. Click a provider card (Gemini, OpenAI, Anthropic, or Custom) — the card grid shows a "Key Saved" or "Active" badge once configured.
  3. Paste the API key into the masked input (toggle the eye icon to reveal it) and pick a model from the quick-preset chips, or type a custom model name.
  4. Optionally click Test Connection — this pings the provider without saving anything, and reports latency and the resolved model on success.
  5. Click Save AI Configuration. The key is encrypted and the provider becomes active immediately — the status pill in the panel header switches from "Demo Mode" to "Live AI Active".
  6. Clear Key removes the stored credentials for the current provider and drops the app back into Demo Mode.
Tip: For Gemini, the "Auto-Detect My Key's Models" button calls Google's model-listing endpoint with the pasted key and replaces the preset chips with the models actually available to that key/account.

Security & Key Storage

Because Luxuria has no backend, an API key has to live somewhere in the browser. The template takes the strongest practical approach available client-side:

Mechanism Detail
Encryption Every key is encrypted with AES-GCM 256-bit via the browser's native Web Crypto API before it ever touches storage.
Key material The AES key itself is generated as non-extractable and stored as a native CryptoKey object inside an IndexedDB database (luxuria-ai-crypto) — it can never be exported as raw bytes.
What's persisted Only ciphertext ({cipher, iv}) is written to localStorage. The plaintext API key exists only in-memory for the current session.
Logout Logging out clears both the persisted ciphertext (normal storage clear) and the in-memory decrypted key, so a shared/public machine never keeps a live key after sign-out.
Fallback If IndexedDB or Web Crypto is unavailable in a given browser, the template falls back to plaintext storage with a visible console warning + UI banner, so the feature still works everywhere.
Be aware of the limits: encryption at rest stops a casual localStorage dump, DevTools screenshot, or backup file from exposing a raw key. It does not protect against a genuine XSS attack running same-origin script, since that script could call the Web Crypto API itself. This architecture is intended for personal or development use. For a shared, production, multi-user deployment, route AI calls through your own backend proxy instead of calling providers directly from the browser.

Demo Mode

Every AI action checks whether an active key is configured before doing anything. If it isn't, no network request is made at all — the action resolves instantly using a canned fixture from src/assets/data/ai-demo/ and the result is visually tagged with a "Demo" badge/pill (never a colored left-border, per the template's card styling convention).

This makes Demo Mode the default, safe experience: reviewers, clients, or anyone previewing the template see fully working AI features immediately, with zero setup, zero cost, and zero risk of a stray API call.

Fixture file Used by
chat-demo.json AI Assistant (full page & header panel)
booking-insight-demo.json Bookings anomaly detection
maintenance-triage-demo.json Maintenance "AI Suggest Priority"
chart-explain-demo.json Revenue Report "Explain with AI"

AI Assistant Concierge

The AI Assistant lets any signed-in user (Admin or Employee) ask free-form questions and get an answer in a chat interface, in two places that share the exact same conversation:

Surface Description
Full page Route /ai-assistant. A dedicated "Luxuria Executive Assistant" page with suggested-prompt chips, a full message history, and a live/demo status badge in the header. Available to Admin and Employee roles.
Header quick-panel A small "AI Concierge" icon in the top header opens a compact popover (same widget pattern as the Ctrl+K Command Palette) for a quick question without leaving the current page, with a link to open the full page.

Because both surfaces read from one shared, in-memory conversation service, a question asked in the popover is still visible if the user then opens the full Assistant page.

Conversation history is kept in memory only for the current session — it is not persisted, and clears on page reload or logout.

Flagship AI Integrations

Beyond the Assistant, three existing admin pages have a real, working AI action wired directly into their UI. These double as the reference pattern for adding AI to any other page — see the developer guide further down.

Page Trigger What it does
Bookings
All Bookings
"AI Anomaly Detection" button above the bookings table Summarizes the currently loaded bookings into a short narrative and bullet list flagging unpaid, overdue, or otherwise unusual reservations.
Maintenance
Service Requests
"AI Suggest Priority" in each row's action menu Looks at one maintenance request (category, description, room) and suggests an urgency/priority with a short justification, shown inline for that row.
Reports
Revenue Report
"Explain with AI" icon on the Revenue Trend chart header Generates a plain-language narrative of the chart's series data — what's trending up/down and why it might matter — rendered as a callout under the chart.

Adding AI to Another Page

Two shared components make it a small amount of glue code to add a similar AI action to any other page:

  • <app-ai-insight-card> — a full result card (loading / success with bullets & a Demo/AI badge / inline error with a link back to Settings). Good for a summary block above a table or dashboard.
  • <app-ai-explain-button> — a small icon button you drop next to an existing card/chart header; you own where the result is rendered.
                         	
// 1. Inject the provider service in your component
private aiProvider = inject(AiProviderService);
private aiSettings = inject(AiSettingsService);
private cdr = inject(ChangeDetectorRef);

aiLoading = false;
aiResult: AiInsightResponse | null = null;
aiError: AiErrorKind | null = null;

async loadAiInsights(): Promise<void> {
  this.aiLoading = true;
  this.cdr.markForCheck(); // zoneless -- required after any async state change

  const res = await this.aiProvider.insight({
    kind: 'summary',       // or 'anomaly' | 'sentiment' | 'triage' | 'explain-chart'
    prompt: 'Summarize this data for a hotel manager',
    data: this.dataSource.data,
  });

  if (res.ok) {
    this.aiResult = res.data;   // res.data.demo tells you if it was a canned response
  } else {
    this.aiError = res.error.kind; // 'no-key' | 'network' | 'auth' | 'rate-limit' | ...
  }

  this.aiLoading = false;
  this.cdr.markForCheck();
}
                                    
                        		
                         	
<app-ai-insight-card
  title="My Section Insights"
  [loading]="aiLoading"
  [result]="aiResult"
  [error]="aiError"
  (refresh)="loadAiInsights()">
</app-ai-insight-card>
                                    
                        		
Every call resolves to a typed AiResult<T> ({ ok: true, data, demo } or { ok: false, error }) and never throws — you never need a try/catch around the AI call itself, only a demo/error/success branch in your template. Add a new kind and a matching JSON fixture under assets/data/ai-demo/ if your use case doesn't fit the existing kinds.

Routes, Menu & Access

Route Menu icon Allowed roles
/ai-settings smart_toy Role.Admin only
/ai-assistant auto_awesome Role.Admin, Role.Employee

Both entries are defined in src/assets/data/menu.json and their routes in src/app/modules/app.routes.ts, following the exact same AuthGuard + data: { role: [...] } pattern used by every other route in the app — see the Role Configurations page for how that works.