{{ allMenuItems.find(item => item.id === currentView)?.name || 'Dashboard' }}
{{ currentEnvironment }}⚠️ Production Environment
You are managing the PRODUCTION environment. Changes will affect live data. Please be careful!
Total Products
{{ stats.products }}
Total Doctors
{{ stats.doctors }}
Total Pharmacies
{{ stats.pharmacies }}
Loaded orders
{{ stats.orders }}
Total Users
{{ stats.users }}
Quick Actions
Slack Error Logging Test
Developer Tools
Test that Slack error notifications are working correctly. Messages will be sent to #soos-errors.
{{ slackTestResult.message }}
| Image | Product Name | Category | Manufacturer | Pharmacies | Variants | Status | Actions |
|---|---|---|---|---|---|---|---|
|
|
{{ template.displayName }}
{{ template.cultivar || '-' }}
|
{{ template.category || '-' }} | {{ template.manufacturer || '-' }} | {{ template.pharmacyIds == null ? 'All' : template.pharmacyIds.length === 0 ? '—' : template.pharmacyIds.map(id => pharmacyNameById[id] || id).join(', ') }} |
{{ template.cannaflowIds?.length || 0 }}
({{ getAvailableVariantCount(template) }} available)
|
{{ templateStatus(template).label }} |
{{ selectedTemplate.displayName }}
{{ templateVariants.length }} variant{{ templateVariants.length !== 1 ? 's' : '' }}
{{ editingVariant ? 'Edit Variant' : 'Add New Variant' }}
From CannaFlow, refreshed on every sync
From CannaFlow, refreshed on every sync
Stock status is controlled by the CannaFlow sync. To take this variant out of the shop, delete it or disable the template.
No variants found for this template
| Name | Specialty | Phone | Actions | |
|---|---|---|---|---|
|
{{ doctor.name }}
|
{{ doctor.specialty }} | {{ doctor.email }} | {{ doctor.phone }} |
| Logo | Name | Storefront | Phone | Address | Portal | Actions | |
|---|---|---|---|---|---|---|---|
|
|
{{ pharmacy.name }}
|
{{ pharmacy.displayName || '—' }} | {{ pharmacy.email }} | {{ pharmacy.phone }} | {{ pharmacy.street }}, {{ pharmacy.city }} {{ pharmacy.zip }} | {{ pharmacy.portalUserEmail || '—' }} |
Catalog Sync
Updates prices & availability from pharmacy catalog
{{ catalogSyncResult.stats?.variantsUpdated || 0 }}
Updated
{{ catalogSyncResult.stats?.variantsCreated || 0 }}
Created
{{ catalogSyncResult.stats?.variantsUnavailable || 0 }}
Unavailable
Registry Sync
Scans full Cannaflow registry for new variants
{{ registrySyncResult.stats?.registryProductsScanned || 0 }}
Products Scanned
{{ registrySyncResult.stats?.newVariantsDetected || 0 }}
New Variants Found
⚠️ Broken products skipped:
{{ registrySyncResult.stats.brokenProducts.join(', ') }}
Pharmacy Catalogue Import
Imports every product a pharmacy lists on Cannaflow, creating missing templates and variants. Runs automatically at 04:00 only when CANNAFLOW_DAILY_IMPORT_ENABLED is set. Takes about a minute per pharmacy.
Talking to Cannaflow — this can take a minute per pharmacy.
{{ pharmacyImport.result.message }}
{{ pharmacyImport.result.stats?.templatesCreated || 0 }}
Templates {{ pharmacyImport.result.dryRun ? 'to create' : 'created' }}
{{ pharmacyImport.result.stats?.variantsCreated || 0 }}
Variants {{ pharmacyImport.result.dryRun ? 'to create' : 'created' }}
{{ pharmacyImport.result.stats?.variantsAttachedToExistingTemplates || 0 }}
Variants {{ pharmacyImport.result.dryRun ? 'to attach' : 'attached' }} to existing templates
{{ pharmacyImport.result.stats?.templatesOpenedForPharmacy || 0 }}
Templates {{ pharmacyImport.result.dryRun ? 'to open' : 'opened' }} for this pharmacy
{{ pharmacyImport.result.stats?.templateImagesBackfilled || 0 }}
Images backfilled
{{ pharmacyImport.result.stats?.skippedExisting || 0 }}
Already present
{{ pharmacyImport.result.stats?.errors || 0 }}
Errors
First errors:
{{ e.cannaflowId }}: {{ e.error }}
Sync Status
Templates
{{ variantSyncStatus.templatesCount || 0 }}
Active Variants
{{ variantSyncStatus.activeVariantsCount || 0 }}
Last Catalog Sync
{{ variantSyncStatus.lastCatalogSync ? formatDateTime(variantSyncStatus.lastCatalogSync) : 'Never' }}
Last Registry Scan
{{ variantSyncStatus.lastRegistryScan ? formatDateTime(variantSyncStatus.lastRegistryScan) : 'Never' }}
Click refresh to load sync status
Catalog Sync
- • Fetches products from pharmacy catalog
- • Updates prices, availability, stock
- • Runs automatically every 10 minutes
- • Only updates existing variants
Registry Sync
- • Scans full Cannaflow registry
- • Detects new variants for templates
- • Sends email notifications
- • Handles broken products gracefully
| Order ID | Doctor | Pharmacy | User | Status | Items | Created | Invoice | Rx | Actions | |
|---|---|---|---|---|---|---|---|---|---|---|
|
{{ order.id.substring(0, 8) }}...
|
{{ getDoctorName(order.doctorId) }} | {{ order.pharmacyId === null || order.pharmacy?.isPostal ? 'Postal' : (order.pharmacy?.name || pharmacyNameById[order.pharmacyId] || '—') }} | {{ getUserDisplayName(order.userId) }} |
DEMO
{{ getOrderStatusLabel(order.status) }}
⚠️
✒️❌
|
{{ order.items?.length || 0 }} items | {{ formatDate(order.createdAt) }} |
|
| ID | Name | Phone | Birthdate | Actions | |
|---|---|---|---|---|---|
|
{{ user.id }}
|
{{ user.fullName || `${user.firstName || ''} ${user.lastName || ''}`.trim() || 'N/A' }}
|
{{ user.email || 'N/A' }} | {{ user.phoneNumber || 'N/A' }} | {{ user.birthdate || 'N/A' }} |
Bulk Invoice Generation
Generate and download invoices for all orders within a specified date range.
Regular Invoices
This will generate invoices for all orders created between {{ new Date(invoiceStartDate).toLocaleDateString() }} and {{ new Date(invoiceEndDate).toLocaleDateString() }}. All invoices will be downloaded as a ZIP file.
GOA Invoices (German Customers)
This will generate GOA invoices for all orders created between {{ new Date(goaInvoiceStartDate).toLocaleDateString() }} and {{ new Date(goaInvoiceEndDate).toLocaleDateString() }}. All GOA invoices will be downloaded as a ZIP file.
Orders placed by these emails run through the demo flow: no payment is taken, the order is auto-approved, and a voided (watermarked) prescription is emailed to the patient. CannaFlow and the e-signature providers are never contacted. Demo orders are excluded from reporting.
Add Demo User
| Note | Added | Actions | |
|---|---|---|---|
| Loading… | |||
| No demo users yet. | |||
| {{ demo.email }} | {{ demo.note || '—' }} | {{ demo.createdAt ? new Date(demo.createdAt).toLocaleDateString() : '—' }} | |
Total Vouchers
{{ voucherAnalytics?.totalVouchers || 0 }}
Active Vouchers
{{ voucherAnalytics?.activeVouchers || 0 }}
Total Redemptions
{{ voucherAnalytics?.totalRedemptions || 0 }}
Total Discount Given
€{{ ((voucherAnalytics?.totalDiscountGiven || 0) / 100).toFixed(2) }}
Manage Vouchers
| Code | Type | Value | Applies To | Usage | Status | Actions |
|---|---|---|---|---|---|---|
|
{{ voucher.code }}
{{ voucher.description }}
|
{{ voucher.type }} | {{ voucher.value }}% €{{ (voucher.value / 100).toFixed(2) }} | {{ voucher.appliesTo }} Initial only | {{ voucher.usageCount || 0 }} / {{ voucher.usageLimit }} / ∞ | {{ voucher.active ? 'Active' : 'Inactive' }} |
Redemption Statistics
| Voucher Code | Redemptions | Total Discount | Status |
|---|---|---|---|
| {{ stat.code }} | {{ stat.redemptions }} | €{{ (stat.totalDiscount / 100).toFixed(2) }} | {{ stat.active ? 'Active' : 'Inactive' }} |
Rx Products
Non-cannabis prescription medicines. Prices are set per pharmacy.
- {{ p.line === null ? '' : 'line ' + p.line + ': ' }}{{ p.problem }}
| Line | Action | Product | Price | In shop | Dosage text | Other changes |
|---|---|---|---|---|---|---|
| {{ c.line }} | {{ c.action }} | {{ c.after.name }} {{ c.after.strength }} | {{ c.before.priceCent === null ? '—' : formatEur(c.before.priceCent) }}{{ formatEur(c.after.priceCent) }} | {{ c.after.onSale ? 'yes' : 'no — hidden' }} |
{{ c.before.dosageText }} {{ c.after.dosageText }}
|
{{ rxImportOtherChanges(c) }} |
| Product | Category | PZN | Pharmacy prices | Sort | Status | Actions |
|---|---|---|---|---|---|---|
| No rx products yet. | ||||||
|
{{ product.name }}
{{ product.activeIngredient }} · {{ product.strength }} · {{ product.form }} · {{ product.packSize }}
{{ product.slug }}
|
{{ product.categoryKey }} | {{ product.pzn }} |
No pharmacy
{{ pharmacyNameById[offer.pharmacyId] || offer.pharmacyId }}: {{ formatEur(offer.priceCent) }}
|
{{ product.sorting }} | {{ product.active ? 'Active' : 'Inactive' }} | |
Loading report data...
{{ reportingError }}
Total Orders
{{ reportingData.summary?.totalOrders || 0 }}
Paid Orders
{{ reportingData.summary?.paidOrders || 0 }}
Revenue
€{{ Math.round((reportingData.summary?.totalRevenue || 0) / 100) }}
Total Grams
{{ reportingData.summary?.totalGrams || 0 }}g
Active Users
{{ reportingData.summary?.activeUsers || 0 }}
KPIs
{{ reportingData.summary?.totalKPIs || 0 }}
Click to view details
Top 5 Brands by Grams
Top 5 Strains by Grams
Revenue by Category
User Age Distribution
Order Status
Top 10 Pharmacies
| Rank | Pharmacy | Orders | Grams | Revenue | % Share |
|---|---|---|---|---|---|
| {{ index + 1 }} | {{ pharmacy.name }} | {{ pharmacy.orders }} | {{ pharmacy.grams }}g | €{{ Math.round(pharmacy.revenue / 100) }} | {{ pharmacy.percentage?.toFixed(1) }}% |
User Behavior Metrics
Product Categories
No Report Data
Select a week and click Refresh to load the report.