Loading...

Admin Dashboard

Sign in to manage your telemedicine platform

{{ error }}

{{ allMenuItems.find(item => item.id === currentView)?.name || 'Dashboard' }}

{{ currentEnvironment }}
{{ new Date().toLocaleString() }}

⚠️ 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 }}

Showing {{ filteredTemplates.length }} of {{ templates.length }} templates
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' }}

= {{ (variantFormData.price / 100).toFixed(2) }} EUR

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

{{ variant.name }}
{{ !variant.active ? 'Inactive' : variant.availability ? 'Available' : 'Unavailable' }} {{ pharmacyNameById[variant.pharmacyId] || variant.pharmacyId || '—' }} Hidden by pharmacy
THC: {{ variant.thc }}%
CBD: {{ variant.cbd }}%
Price: {{ typeof variant.price === 'number' ? (variant.price / 100).toFixed(2) : (parseFloat(variant.price) / 100 || 0).toFixed(2) }} EUR
ID: {{ variant.cannaflowId }}
Qty Inc: {{ variant.quantityIncrement || '-' }}
Min Order: {{ variant.minOrderQuantity || '-' }}
{{ variant.country }} Irradiated
Last synced: {{ new Date(variant.lastSyncedAt).toLocaleString() }}
Name Specialty Email Phone Actions
{{ doctor.name }}
{{ doctor.specialty }} {{ doctor.email }} {{ doctor.phone }}
Logo Name Storefront Email 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

Last Result {{ catalogSyncResult.success ? 'Success' : 'Failed' }}

{{ catalogSyncResult.stats?.variantsUpdated || 0 }}

Updated

{{ catalogSyncResult.stats?.variantsCreated || 0 }}

Created

{{ catalogSyncResult.stats?.variantsUnavailable || 0 }}

Unavailable

Templates: {{ catalogSyncResult.stats?.templatesProcessed || 0 }} Duration: {{ catalogSyncResult.stats?.duration || 0 }}ms
⚠️ {{ catalogSyncResult.stats.newVariantsDetected }} new variants detected in registry!

Registry Sync

Scans full Cannaflow registry for new variants

Last Result {{ registrySyncResult.success ? 'Success' : 'Failed' }}

{{ registrySyncResult.stats?.registryProductsScanned || 0 }}

Products Scanned

{{ registrySyncResult.stats?.newVariantsDetected || 0 }}

New Variants Found

⚠️ Broken products skipped:

{{ registrySyncResult.stats.brokenProducts.join(', ') }}

Duration: {{ registrySyncResult.stats?.duration || 0 }}ms

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.dryRun ? 'Preview (nothing written)' : 'Imported' }} — {{ pharmacyImport.result.pharmacyName }} {{ pharmacyImport.result.success ? (pharmacyImport.result.dryRun ? 'Dry run' : 'Success') : 'Failed' }}

{{ pharmacyImport.result.message }}

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
Filtered by User: {{ getUserDisplayName(userIdFilter) }} ({{ userIdFilter }})
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 Email 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

Email 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.

Export current sheet
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' }}
Quick Select:
{{ calculateDaysInRange() }} days selected
Selected Period: {{ formatDisplayDate(reportDateRange.startDate) }} – {{ formatDisplayDate(reportDateRange.endDate) }} {{ reportPresets.find(p => p.id === reportDateRange.preset)?.label }}
Comparing with previous {{ calculateDaysInRange() }} days

Loading report data...

{{ reportingError }}

Total Orders

{{ reportingData.summary?.totalOrders || 0 }}

{{ getTrendIcon(reportingData.stats?.orders?.total, getPreviousStats()?.orders?.total) }} {{ getTrendValue(reportingData.stats?.orders?.total, getPreviousStats()?.orders?.total) }} vs previous period

Paid Orders

{{ reportingData.summary?.paidOrders || 0 }}

{{ getTrendIcon(reportingData.stats?.orders?.paid, getPreviousStats()?.orders?.paid) }} {{ getTrendValue(reportingData.stats?.orders?.paid, getPreviousStats()?.orders?.paid) }} vs previous period

Revenue

€{{ Math.round((reportingData.summary?.totalRevenue || 0) / 100) }}

{{ getTrendIcon(reportingData.stats?.revenue?.paid, getPreviousStats()?.revenue?.paid) }} {{ getTrendValue(reportingData.stats?.revenue?.paid, getPreviousStats()?.revenue?.paid) }} vs previous period

Total Grams

{{ reportingData.summary?.totalGrams || 0 }}g

{{ getTrendIcon(reportingData.stats?.flowers?.grams, getPreviousStats()?.flowers?.grams) }} {{ getTrendValue(reportingData.stats?.flowers?.grams, getPreviousStats()?.flowers?.grams) }} vs previous period

Active Users

{{ reportingData.summary?.activeUsers || 0 }}

{{ getTrendIcon(reportingData.stats?.users?.activeInRange || reportingData.stats?.users?.activeThisWeek, getPreviousStats()?.users?.activeInRange || getPreviousStats()?.users?.activeThisWeek) }} {{ getTrendValue(reportingData.stats?.users?.activeInRange || reportingData.stats?.users?.activeThisWeek, getPreviousStats()?.users?.activeInRange || getPreviousStats()?.users?.activeThisWeek) }} vs previous period

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

New Users {{ reportingData.stats?.users?.newThisWeek ?? reportingData.stats?.users?.newInRange ?? 0 }}
Active Users {{ reportingData.stats?.users?.activeThisWeek ?? reportingData.stats?.users?.activeInRange ?? 0 }}
Avg Orders per User {{ reportingData.stats?.users?.avgOrdersPerUser?.toFixed(2) || 0 }}
First-time Customers (placing their first order ever) {{ reportingData.stats?.users?.firstTimeCustomers ?? 0 }} ({{ reportingData.stats?.users?.firstTimeCustomersPercentage?.toFixed(1) || 0 }}%)
Returning Customers (have ordered before) {{ reportingData.stats?.users?.returningCustomers ?? 0 }} ({{ reportingData.stats?.users?.returningCustomersPercentage?.toFixed(1) || 0 }}%)
Avg Revenue per User €{{ Math.round((reportingData.stats?.users?.avgRevenuePerUser || 0) / 100) }}
Avg Grams per User {{ reportingData.stats?.users?.avgGramsPerUser?.toFixed(1) || 0 }}g

Product Categories

Flowers {{ reportingData.stats?.flowers?.orders || 0 }} different strains
{{ reportingData.stats?.flowers?.grams || 0 }}g €{{ Math.round((reportingData.stats?.flowers?.revenue || 0) / 100) }} €{{ (reportingData.stats?.flowers?.avgPricePerGram / 100)?.toFixed(2) || '0.00' }}/g
Extracts {{ reportingData.stats?.extracts?.orders || 0 }} different strains
{{ reportingData.stats?.extracts?.ml || 0 }}ml €{{ Math.round((reportingData.stats?.extracts?.revenue || 0) / 100) }} €{{ (reportingData.stats?.extracts?.avgPricePerMl / 100)?.toFixed(2) || '0.00' }}/ml
Edibles {{ reportingData.stats?.edibles?.orders || 0 }} different strains
{{ reportingData.stats?.edibles?.pieces || 0 }} pieces €{{ Math.round((reportingData.stats?.edibles?.revenue || 0) / 100) }} €{{ (reportingData.stats?.edibles?.avgPricePerPiece / 100)?.toFixed(2) || '0.00' }}/pc
Drinkables {{ reportingData.stats?.drinkables?.orders || 0 }} orders
{{ Math.round((reportingData.stats?.drinkables?.ml || 0) / 60) }} pkg ({{ reportingData.stats?.drinkables?.ml || 0 }}ml) €{{ ((reportingData.stats?.drinkables?.revenue || 0) / 100).toFixed(2) }} €{{ ((reportingData.stats?.drinkables?.avgPricePerMl || 0) * 60 / 100).toFixed(2) }}/pkg
Report Period: {{ reportingData.period?.start ? new Date(reportingData.period.start).toLocaleDateString() : 'N/A' }} - {{ reportingData.period?.end ? new Date(reportingData.period.end).toLocaleDateString() : 'N/A' }} | Generated: {{ reportingData.generatedAt ? new Date(reportingData.generatedAt).toLocaleString() : 'N/A' }}

No Report Data

Select a week and click Refresh to load the report.

Access Denied

You don't have the required role to access this dashboard.

Valid roles: admin, content, reporting

Contact an administrator to request access.

Register New Doctor

A random password will be generated and shown after registration

Login Created

Login created successfully

Email: {{ registeredDoctorInfo.email }}

UID: {{ registeredDoctorInfo.uid }}

Important: Save this password!

This is the only time you'll see this password. Please copy it and send it to the recipient securely.

{{ registeredDoctorInfo.password }}

Change Password for {{ selectedDoctorForPassword?.firstName }} {{ selectedDoctorForPassword?.lastName }}

Choose to either generate a random password or set a custom one

OR

Minimum 8 characters

Doctor Statistics

Loading statistics...

{{ doctorStats.doctorName }}

{{ doctorStats.totalOrders }}

Total Treated

{{ doctorStats.totalInitial }}

Initial

{{ doctorStats.totalFollowUp }}

Follow-Up

Month Total Initial Follow-Up
{{ stat.month }} {{ stat.year }} {{ stat.total }} {{ stat.initial }} {{ stat.followUp }}
No orders found for this doctor

Cancel Order

Order ID:

{{ cancelOrderData.id }}

Customer:

{{ cancelOrderData.user?.email || 'N/A' }}

Created:

{{ formatDate(cancelOrderData.createdAt) }}

Warning

This action will cancel the order in CannaFlow and send a cancellation email to the customer. This action cannot be undone.

{{ (editingItem || editingTemplate) ? 'Edit' : 'Add' }} {{ currentView === 'templates' ? 'Template' : currentView === 'doctors' ? 'Doctor' : currentView === 'pharmacies' ? 'Pharmacy' : currentView === 'users' ? 'User' : currentView === 'vouchers' ? 'Voucher' : currentView === 'rx-products' ? 'Rx Product' : currentView.slice(0, -1) }}

{{ selectedProduct?.isPharmacy ? 'Upload Pharmacy Logo' : 'Upload Product Images' }}

{{ selectedProduct.isPharmacy ? 'Pharmacy:' : 'Product:' }} {{ selectedProduct.name }}

Abbreviation: {{ selectedProduct.abbreviation }}

Logo

Product Gallery ({{ (selectedProduct.images || []).length }} image{{ (selectedProduct.images || []).length !== 1 ? 's' : '' }}, {{ (selectedProduct.videos || []).length }} video{{ (selectedProduct.videos || []).length !== 1 ? 's' : '' }})

High Low

{{ imageUrl.split('/').pop() }}

Video

{{ videoUrl.split('/').pop() }}


{{ selectedProduct.isPharmacy ? 'No logo uploaded yet' : 'No images uploaded yet' }}

Upload high-res and low-res images. Name files with "_h" or "_high" for high-res, "_l" or "_low" for low-res.
GIFs (max 10 MB) and MP4 videos (H.264, max 50 MB) are gallery-only and never become the main image. For short loops prefer MP4 over a large GIF. iPhone: Settings > Camera > Formats > "Most Compatible".

Selected Files ({{ imageFiles.length }})

  • {{ file.name }} ({{ (file.size / 1024).toFixed(1) }} KB)

Order Details

{{ selectedOrderDetails.id }}
{{ selectedOrderDetails.status.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()) }}
Reason: {{ selectedOrderDetails.cannaflow.cancellationReason }}
Cancelled by: {{ selectedOrderDetails.cannaflow.cancelledBy === 'pharmacy' ? 'Pharmacy' : 'Telemedicine provider' }}
Refunded: €{{ (selectedOrderDetails.cannaflow.refundedAmountCent / 100).toFixed(2) }}
{{ selectedOrderDetails.userId }}
Name: {{ selectedOrderDetails.user.name || selectedOrderDetails.user.fullName }}
Email: {{ selectedOrderDetails.user.email }}
{{ getDoctorName(selectedOrderDetails.doctorId) || selectedOrderDetails.doctorId || 'N/A' }}
{{ formatDate(selectedOrderDetails.createdAt) }}
{{ formatDate(selectedOrderDetails.updatedAt) }}
{{ item.name || item.productName || 'Unknown Product' }}
Quantity: {{ item.quantity }}
Price: €{{ (item.price / 100).toFixed(2) }}
€{{ ((item.price * item.quantity) / 100).toFixed(2) }}
No items in this order
{{ selectedOrderDetails.pharmacy.name || 'N/A' }}
{{ selectedOrderDetails.pharmacy.street }}
{{ selectedOrderDetails.pharmacy.zip }} {{ selectedOrderDetails.pharmacy.city }}
{{ selectedOrderDetails.pharmacy.email }}
Prescription ID: {{ selectedOrderDetails.cannaflow.prescriptionId }}
Checkout URL: View Checkout
Transmission Status: {{ selectedOrderDetails.cannaflowTransmissionStatus }}
{{ signedPrescriptionError }}
View Raw JSON Data
{{ JSON.stringify(selectedOrderDetails, null, 2) }}

KPI Reference Guide

All Key Performance Indicators tracked in reporting (~130 metrics)

ORDERS (6 KPIs)

KPI Name KPI ID Description
Total Ordersorders_totalAll orders created in the period
Total Paid Ordersorders_paid_totalOrders with confirmed payment
Total Approved Ordersorders_approved_totalOrders approved by doctor
Total Declined Ordersorders_declined_totalOrders declined by doctor
Total Cancelled Ordersorders_cancelled_totalOrders cancelled by user/admin
Total Pending Ordersorders_pending_totalOrders awaiting action

REVENUE (2 KPIs)

KPI Name KPI ID Description
Total Paid Revenue (EUR)revenue_paid_totalRevenue from paid orders
Total Approved Revenue (EUR)revenue_approved_totalRevenue from approved orders

FLOWERS (4 KPIs)

KPI Name KPI ID Description
Flower Orders Totalflowers_orders_totalNumber of flower line items
Flower Revenue Total (EUR)flowers_revenue_totalTotal revenue from flowers
Flower Grams Totalflowers_grams_totalTotal grams sold
Avg Price per Gram (EUR/g)flowers_avg_price_per_gramAverage price per gram

EXTRACTS (4 KPIs)

KPI Name KPI ID Description
Extract Orders Totalextracts_orders_totalNumber of extract line items
Extract Revenue Total (EUR)extracts_revenue_totalTotal revenue from extracts
Extract ml Totalextracts_ml_totalTotal milliliters sold
Avg Price per ml (EUR/ml)extracts_avg_price_per_mlAverage price per ml

EDIBLES (4 KPIs)

KPI Name KPI ID Description
Edibles Orders Totaledibles_orders_totalNumber of edible line items
Edibles Revenue Total (EUR)edibles_revenue_totalTotal revenue from edibles
Edibles Pieces Totaledibles_pieces_totalTotal pieces sold
Avg Price per piece (EUR/piece)edibles_avg_price_per_pieceAverage price per piece

TOP BRANDS (21 KPIs)

For each of the top 5 brands: Name, Grams, % of Total, Revenue

KPI Pattern KPI ID Pattern Description
TOP #N Brand - Nametop_brand_N_nameBrand name (N = 1-5)
TOP #N Brand - Gramstop_brand_N_gramsGrams sold by brand
TOP #N Brand - % of Totaltop_brand_N_grams_pctPercentage of total grams
TOP #N Brand - Revenue (EUR)top_brand_N_revenueRevenue from brand
TOP 5 Brands - % of Totaltop_5_brands_grams_pctCombined % of top 5 brands

TOP STRAINS (21 KPIs)

For each of the top 5 strains: Name, Grams, % of Total, Revenue

KPI Pattern KPI ID Pattern Description
TOP #N Strain - Nametop_strain_N_nameStrain/cultivar name (N = 1-5)
TOP #N Strain - Gramstop_strain_N_gramsGrams sold of strain
TOP #N Strain - % of Totaltop_strain_N_grams_pctPercentage of total grams
TOP #N Strain - Revenue (EUR)top_strain_N_revenueRevenue from strain
TOP 5 Strains - % of Totaltop_5_strains_grams_pctCombined % of top 5 strains

TOP PHARMACIES (50 KPIs)

For each of the top 10 pharmacies: Name, Orders, Grams, Revenue, % of Revenue

KPI Pattern KPI ID Pattern Description
TOP #N Pharmacy - Nametop_pharmacy_N_namePharmacy name (N = 1-10)
TOP #N Pharmacy - Orderstop_pharmacy_N_ordersNumber of orders
TOP #N Pharmacy - Gramstop_pharmacy_N_gramsTotal grams dispensed
TOP #N Pharmacy - Revenue (EUR)top_pharmacy_N_revenueRevenue from pharmacy
TOP #N Pharmacy - % of Revenuetop_pharmacy_N_revenue_pctPercentage of total revenue

USER DEMOGRAPHICS (11 KPIs)

KPI Name KPI ID Description
Users Age 18-25users_age_18_25Users in age group 18-25
Users Age 26-35users_age_26_35Users in age group 26-35
Users Age 36-45users_age_36_45Users in age group 36-45
Users Age 46-55users_age_46_55Users in age group 46-55
Users Age 56+users_age_56_plusUsers age 56 and older
Top 6 Cities (users_location_1 to users_location_6) - User count per city

USER BEHAVIOR (7 KPIs)

KPI Name KPI ID Description
New Users This Weekusers_new_this_weekUsers who registered in the period
Active Users This Weekusers_active_this_weekUnique users who placed orders
Avg Orders per Userusers_avg_orders_per_userTotal orders / active users
Repeat Usersusers_repeat_countUsers who ordered multiple days in period
Repeat Users (%)users_repeat_pctPercentage of repeat users
Avg Revenue per User (EUR)users_avg_revenue_per_userTotal revenue / active users
Avg Grams per Userusers_avg_grams_per_userTotal grams / active users

Total KPIs Summary

Orders: 6
Revenue: 2
Flowers: 4
Extracts: 4
Edibles: 4
Top Brands: 21
Top Strains: 21
Top Pharmacies: 50
Demographics: 11
User Behavior: 7
Total: ~130 KPIs