astro-component-kit 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +133 -36
- package/package.json +17 -4
- package/src/components/index.ts +18 -202
- package/src/components/lib/Galaxy3D.astro +249 -137
- package/src/components/lib/app/AppSidebar.astro +249 -0
- package/src/components/lib/app/AppTopbar.astro +284 -0
- package/src/components/lib/app/ArrivalsBoard.astro +176 -0
- package/src/components/lib/app/AuthCard.astro +213 -0
- package/src/components/lib/app/DataTable.astro +346 -0
- package/src/components/lib/app/GuestProfileCard.astro +133 -0
- package/src/components/lib/app/HousekeepingBoard.astro +189 -0
- package/src/components/lib/app/PageHeader.astro +157 -0
- package/src/components/lib/app/PlanUsageCard.astro +112 -0
- package/src/components/lib/app/RoomRack.astro +207 -0
- package/src/components/lib/app/SettingsSection.astro +96 -0
- package/src/components/lib/app/SwitchRow.astro +71 -0
- package/src/components/lib/app/TeamMembers.astro +190 -0
- package/src/components/lib/app/app-icons.ts +52 -0
- package/src/components/lib/app/index.ts +19 -0
- package/src/components/lib/buttons/NeonBorderButton.astro +162 -0
- package/src/components/lib/buttons/RippleButton.astro +199 -0
- package/src/components/lib/buttons/SlideRevealButton.astro +159 -0
- package/src/components/lib/buttons/index.ts +4 -0
- package/src/components/lib/cards/GithubRepoCard.astro +176 -0
- package/src/components/lib/cards/PricingCompareMiniCard.astro +196 -0
- package/src/components/lib/cards/StatsTrendCard.astro +149 -0
- package/src/components/lib/cards/index.ts +3 -0
- package/src/components/lib/charts/ActivityRings.astro +105 -0
- package/src/components/lib/charts/AreaChart.astro +4 -4
- package/src/components/lib/charts/BarChart.astro +3 -3
- package/src/components/lib/charts/BoxPlotChart.astro +109 -0
- package/src/components/lib/charts/BubbleChart.astro +101 -0
- package/src/components/lib/charts/BulletChart.astro +160 -0
- package/src/components/lib/charts/BumpChart.astro +92 -0
- package/src/components/lib/charts/CandlestickChart.astro +105 -0
- package/src/components/lib/charts/ChartFrame.astro +373 -0
- package/src/components/lib/charts/DivergingBarChart.astro +98 -0
- package/src/components/lib/charts/DonutChart.astro +2 -2
- package/src/components/lib/charts/DumbbellChart.astro +92 -0
- package/src/components/lib/charts/ForecastLineChart.astro +125 -0
- package/src/components/lib/charts/FunnelChart.astro +122 -0
- package/src/components/lib/charts/GaugeChart.astro +111 -0
- package/src/components/lib/charts/GoalProgressChart.astro +97 -0
- package/src/components/lib/charts/GroupedBarChart.astro +125 -0
- package/src/components/lib/charts/HeatmapChart.astro +131 -0
- package/src/components/lib/charts/HistogramChart.astro +111 -0
- package/src/components/lib/charts/HorizontalBarChart.astro +155 -0
- package/src/components/lib/charts/KpiStatTile.astro +116 -0
- package/src/components/lib/charts/KpiTrendGrid.astro +60 -0
- package/src/components/lib/charts/LineChart.astro +160 -0
- package/src/components/lib/charts/LinearProgressGauge.astro +136 -0
- package/src/components/lib/charts/LollipopChart.astro +100 -0
- package/src/components/lib/charts/MeterGauge.astro +122 -0
- package/src/components/lib/charts/MetricDeltaTable.astro +125 -0
- package/src/components/lib/charts/PieChart.astro +135 -0
- package/src/components/lib/charts/PolarAreaChart.astro +85 -0
- package/src/components/lib/charts/PopulationPyramid.astro +122 -0
- package/src/components/lib/charts/PunchCardChart.astro +89 -0
- package/src/components/lib/charts/RadarChart.astro +94 -0
- package/src/components/lib/charts/RadarMetricChart.astro +168 -0
- package/src/components/lib/charts/RadialBarChart.astro +124 -0
- package/src/components/lib/charts/RangeAreaChart.astro +113 -0
- package/src/components/lib/charts/RetentionCohortTable.astro +126 -0
- package/src/components/lib/charts/SankeyChart.astro +128 -0
- package/src/components/lib/charts/ScatterPlot.astro +139 -0
- package/src/components/lib/charts/SegmentedBarChart.astro +119 -0
- package/src/components/lib/charts/SlopeChart.astro +105 -0
- package/src/components/lib/charts/SparklineChart.astro +87 -0
- package/src/components/lib/charts/StackedAreaChart.astro +130 -0
- package/src/components/lib/charts/StackedBarChart.astro +136 -0
- package/src/components/lib/charts/SunburstChart.astro +103 -0
- package/src/components/lib/charts/TreemapChart.astro +94 -0
- package/src/components/lib/charts/WaffleChart.astro +121 -0
- package/src/components/lib/charts/WaterfallChart.astro +118 -0
- package/src/components/lib/charts/chart-tooltip.ts +75 -0
- package/src/components/lib/charts/chart-utils.ts +271 -0
- package/src/components/lib/charts/index.ts +46 -0
- package/src/components/lib/data/ActivityHeatmapGrid.astro +157 -0
- package/src/components/lib/data/MetricComparisonRow.astro +109 -0
- package/src/components/lib/data/StatusTimelineFeed.astro +168 -0
- package/src/components/lib/data/index.ts +3 -0
- package/src/components/lib/ecommerce/CartDrawer.astro +483 -0
- package/src/components/lib/ecommerce/CartPageList.astro +290 -0
- package/src/components/lib/ecommerce/CartToast.astro +276 -0
- package/src/components/lib/ecommerce/CategoryTiles.astro +143 -0
- package/src/components/lib/ecommerce/CheckoutForm.astro +423 -0
- package/src/components/lib/ecommerce/CountdownBanner.astro +230 -0
- package/src/components/lib/ecommerce/FilterBar.astro +278 -0
- package/src/components/lib/ecommerce/FilterSidebar.astro +405 -0
- package/src/components/lib/ecommerce/LiveActivityToast.astro +213 -0
- package/src/components/lib/ecommerce/OrderSummary.astro +295 -0
- package/src/components/lib/ecommerce/ProductBuyBox.astro +247 -0
- package/src/components/lib/ecommerce/ProductCardMinimal.astro +229 -0
- package/src/components/lib/ecommerce/ProductGallery.astro +145 -0
- package/src/components/lib/ecommerce/ProductGrid.astro +118 -0
- package/src/components/lib/ecommerce/ProductMedia.astro +113 -0
- package/src/components/lib/ecommerce/ProductRating.astro +78 -0
- package/src/components/lib/ecommerce/ProductToolbar.astro +93 -0
- package/src/components/lib/ecommerce/PromoBar.astro +145 -0
- package/src/components/lib/ecommerce/StockIndicator.astro +127 -0
- package/src/components/lib/ecommerce/StoreHeader.astro +446 -0
- package/src/components/lib/ecommerce/StoreHero.astro +260 -0
- package/src/components/lib/ecommerce/StoreProductCard.astro +307 -0
- package/src/components/lib/ecommerce/TrustBadges.astro +101 -0
- package/src/components/lib/ecommerce/VariantPicker.astro +145 -0
- package/src/components/lib/ecommerce/cart-store.ts +151 -0
- package/src/components/lib/ecommerce/catalog-filter.ts +62 -0
- package/src/components/lib/ecommerce/index.ts +34 -0
- package/src/components/lib/ecommerce/product-icons.ts +28 -0
- package/src/components/lib/ecommerce/types.ts +70 -0
- package/src/components/lib/feedback/ProgressRingCounter.astro +118 -0
- package/src/components/lib/feedback/ToastNotificationGroup.astro +173 -0
- package/src/components/lib/feedback/index.ts +2 -0
- package/src/components/lib/forms/AnimatedInput.astro +4 -2
- package/src/components/lib/forms/CustomRadioGroup.astro +3 -1
- package/src/components/lib/forms/GlassInlineToggleGroup.astro +161 -0
- package/src/components/lib/forms/PasswordStrengthInput.astro +242 -0
- package/src/components/lib/forms/PhoneCountryInput.astro +145 -0
- package/src/components/lib/forms/TagsAutocompleteInput.astro +249 -0
- package/src/components/lib/forms/index.ts +5 -0
- package/src/components/lib/index.ts +2 -0
- package/src/components/lib/layout/BentoGrid3.astro +6 -4
- package/src/components/lib/layout/CenterFixedBox.astro +2 -2
- package/src/components/lib/layout/CenteredContainer.astro +4 -2
- package/src/components/lib/layout/ClusterHorizontal.astro +2 -2
- package/src/components/lib/layout/DiagonalSectionDivider.astro +69 -0
- package/src/components/lib/layout/FeatureHero.astro +4 -2
- package/src/components/lib/layout/FooterCenteredMinimal.astro +4 -2
- package/src/components/lib/layout/FullBleedSection.astro +2 -2
- package/src/components/lib/layout/GalleryGrid4.astro +2 -2
- package/src/components/lib/layout/HolyGrailGrid.astro +127 -0
- package/src/components/lib/layout/LandingSectionSplit.astro +5 -3
- package/src/components/lib/layout/MasonryGrid.astro +2 -2
- package/src/components/lib/layout/MinimalFooterGrid.astro +1 -1
- package/src/components/lib/layout/ScrollSnapSection.astro +1 -1
- package/src/components/lib/layout/SidebarLayoutWrapper.astro +4 -2
- package/src/components/lib/layout/SplitScreenLayout.astro +3 -3
- package/src/components/lib/layout/StepLayoutVertical.astro +1 -1
- package/src/components/lib/layout/StickySidebarWrapper.astro +4 -2
- package/src/components/lib/layout/index.ts +3 -1
- package/src/components/lib/navigation/QuickJumpDock.astro +125 -0
- package/src/components/lib/navigation/StepBreadcrumbBar.astro +151 -0
- package/src/components/lib/navigation/SubNavbarPills.astro +102 -0
- package/src/components/lib/navigation/index.ts +3 -0
- package/src/components/lib/overlays/CookiePreferencesDrawer.astro +226 -0
- package/src/components/lib/overlays/FloatingFeedbackWidget.astro +203 -0
- package/src/components/lib/overlays/ImageCompareSlider.astro +172 -0
- package/src/components/lib/overlays/index.ts +3 -0
- package/src/components/lib/sections/AgencyContactBrief.astro +194 -0
- package/src/components/lib/sections/AgencyFooterCta.astro +94 -0
- package/src/components/lib/sections/AgencyHeader.astro +161 -0
- package/src/components/lib/sections/AgencyHeroKinetic.astro +172 -0
- package/src/components/lib/sections/AgencyServicesList.astro +113 -0
- package/src/components/lib/sections/AgencyTeamGrid.astro +111 -0
- package/src/components/lib/sections/AgencyWorkGrid.astro +147 -0
- package/src/components/lib/sections/BlogGridSection.astro +133 -0
- package/src/components/lib/sections/ButtonsShowcase.astro +20 -18
- package/src/components/lib/sections/CardsShowcase.astro +28 -26
- package/src/components/lib/sections/CareersOpenings.astro +141 -0
- package/src/components/lib/sections/ChangelogSection.astro +151 -0
- package/src/components/lib/sections/ContactGlassForm.astro +10 -8
- package/src/components/lib/sections/ContactSplitMap.astro +141 -0
- package/src/components/lib/sections/CtaAppDownload.astro +13 -11
- package/src/components/lib/sections/CtaGlowBanner.astro +12 -10
- package/src/components/lib/sections/CtaNewsletterSection.astro +165 -0
- package/src/components/lib/sections/CtaSplitImage.astro +149 -0
- package/src/components/lib/sections/DataShowcase.astro +23 -21
- package/src/components/lib/sections/FaqAccordionSection.astro +8 -8
- package/src/components/lib/sections/FaqTwoColumn.astro +121 -0
- package/src/components/lib/sections/FeatureAlternatingRows.astro +192 -0
- package/src/components/lib/sections/FeatureBentoGrid.astro +13 -11
- package/src/components/lib/sections/FeatureIconGrid.astro +155 -0
- package/src/components/lib/sections/FeatureTabsShowcase.astro +182 -0
- package/src/components/lib/sections/FeatureTimelineFlow.astro +8 -6
- package/src/components/lib/sections/FeedbackShowcase.astro +15 -13
- package/src/components/lib/sections/FooterMegaSection.astro +157 -0
- package/src/components/lib/sections/FormsShowcase.astro +22 -20
- package/src/components/lib/sections/HeroDashboardPreview.astro +182 -0
- package/src/components/lib/sections/HeroGlass.astro +22 -20
- package/src/components/lib/sections/HeroGradient.astro +15 -13
- package/src/components/lib/sections/HeroMinimalCentered.astro +175 -0
- package/src/components/lib/sections/HeroShowcaseSplit.astro +306 -0
- package/src/components/lib/sections/HeroSplit.astro +13 -11
- package/src/components/lib/sections/HeroTerminal.astro +253 -0
- package/src/components/lib/sections/HeroWaitlist.astro +231 -0
- package/src/components/lib/sections/HowItWorksSteps.astro +180 -0
- package/src/components/lib/sections/IntegrationsGrid.astro +167 -0
- package/src/components/lib/sections/LayoutShowcase.astro +21 -19
- package/src/components/lib/sections/LogoCloudSection.astro +96 -0
- package/src/components/lib/sections/LogoWallGrid.astro +93 -0
- package/src/components/lib/sections/NavigationShowcase.astro +15 -13
- package/src/components/lib/sections/OverlaysShowcase.astro +21 -19
- package/src/components/lib/sections/PricingComparisonMatrix.astro +15 -13
- package/src/components/lib/sections/PricingSinglePlan.astro +159 -0
- package/src/components/lib/sections/PricingTierCards.astro +11 -9
- package/src/components/lib/sections/PricingToggleCards.astro +171 -0
- package/src/components/lib/sections/ShopCategoryHero.astro +103 -0
- package/src/components/lib/sections/ShopCollectionMosaic.astro +131 -0
- package/src/components/lib/sections/ShopFooterWordmark.astro +100 -0
- package/src/components/lib/sections/ShopHeaderEditorial.astro +396 -0
- package/src/components/lib/sections/ShopHeroEditorial.astro +151 -0
- package/src/components/lib/sections/ShopLookbook.astro +250 -0
- package/src/components/lib/sections/ShopNewsletterEditorial.astro +128 -0
- package/src/components/lib/sections/ShopProductRail.astro +150 -0
- package/src/components/lib/sections/ShopReviewsEditorial.astro +133 -0
- package/src/components/lib/sections/ShopValuesMarquee.astro +91 -0
- package/src/components/lib/sections/StatsCounterBand.astro +124 -0
- package/src/components/lib/sections/StatsDashboardSection.astro +85 -0
- package/src/components/lib/sections/StatsMetricShowcase.astro +9 -7
- package/src/components/lib/sections/TeamGridSection.astro +12 -10
- package/src/components/lib/sections/TestimonialCarousel.astro +167 -0
- package/src/components/lib/sections/TestimonialFeatured.astro +122 -0
- package/src/components/lib/sections/TestimonialGridSection.astro +188 -0
- package/src/components/lib/sections/TestimonialWallGlass.astro +11 -9
- package/src/components/lib/sections/TypographyShowcase.astro +10 -8
- package/src/components/lib/sections/index.ts +39 -0
- package/src/components/lib/templates/ecommerce/StoreCategoryPage.astro +160 -0
- package/src/components/lib/templates/ecommerce/StoreCheckoutPage.astro +98 -0
- package/src/components/lib/templates/ecommerce/StoreHomePage.astro +191 -0
- package/src/components/lib/templates/ecommerce/StoreLayout.astro +169 -0
- package/src/components/lib/templates/ecommerce/StoreProductPage.astro +165 -0
- package/src/components/lib/templates/ecommerce/data.ts +187 -0
- package/src/components/lib/templates/ecommerce/index.ts +14 -0
- package/src/components/lib/templates/ecommerce-editorial/EditorialBagPage.astro +52 -0
- package/src/components/lib/templates/ecommerce-editorial/EditorialCheckoutPage.astro +123 -0
- package/src/components/lib/templates/ecommerce-editorial/EditorialHomePage.astro +102 -0
- package/src/components/lib/templates/ecommerce-editorial/EditorialLayout.astro +216 -0
- package/src/components/lib/templates/ecommerce-editorial/EditorialProductPage.astro +205 -0
- package/src/components/lib/templates/ecommerce-editorial/EditorialShopPage.astro +112 -0
- package/src/components/lib/templates/ecommerce-editorial/data.ts +125 -0
- package/src/components/lib/templates/ecommerce-editorial/index.ts +16 -0
- package/src/components/lib/templates/saas-hotel/AppLayout.astro +83 -0
- package/src/components/lib/templates/saas-hotel/DashboardPage.astro +66 -0
- package/src/components/lib/templates/saas-hotel/GuestsPage.astro +65 -0
- package/src/components/lib/templates/saas-hotel/LandingPage.astro +213 -0
- package/src/components/lib/templates/saas-hotel/ReportsPage.astro +63 -0
- package/src/components/lib/templates/saas-hotel/ReservationsPage.astro +49 -0
- package/src/components/lib/templates/saas-hotel/RoomsPage.astro +48 -0
- package/src/components/lib/templates/saas-hotel/SettingsPage.astro +115 -0
- package/src/components/lib/templates/saas-hotel/SignInPage.astro +50 -0
- package/src/components/lib/templates/saas-hotel/data.ts +229 -0
- package/src/components/lib/templates/saas-hotel/index.ts +19 -0
- package/src/components/lib/templates/saas-hotel/lobby-theme.css +90 -0
- package/src/components/lib/typography/AnimatedCounterText.astro +102 -0
- package/src/components/lib/typography/CurvedPathHeading.astro +86 -0
- package/src/components/lib/typography/GradientTextMarquee.astro +105 -0
- package/src/components/lib/typography/HackerDecodeText.astro +85 -0
- package/src/components/lib/typography/index.ts +4 -0
- package/src/styles/global.css +113 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/components/lib/templates/ecommerce-editorial/index.ts
|
|
2
|
+
// Editorial e-commerce template ("E-commerce 2"): complete store pages built from
|
|
3
|
+
// library sections (ShopHeaderEditorial, ShopHeroEditorial, ShopProductRail…) and components.
|
|
4
|
+
//
|
|
5
|
+
// import { EditorialHomePage } from 'astro-component-kit/templates/ecommerce-editorial';
|
|
6
|
+
// <EditorialHomePage theme="light" />
|
|
7
|
+
|
|
8
|
+
export { default as EditorialLayout } from './EditorialLayout.astro';
|
|
9
|
+
export { default as EditorialHomePage } from './EditorialHomePage.astro';
|
|
10
|
+
export { default as EditorialShopPage } from './EditorialShopPage.astro';
|
|
11
|
+
export { default as EditorialProductPage } from './EditorialProductPage.astro';
|
|
12
|
+
export { default as EditorialBagPage } from './EditorialBagPage.astro';
|
|
13
|
+
export { default as EditorialCheckoutPage } from './EditorialCheckoutPage.astro';
|
|
14
|
+
|
|
15
|
+
export { formaStore, editorialRoutes, editorialProductHref, editorialCategoryHref, pickEditorial } from './data';
|
|
16
|
+
export type { EditorialStore, EditorialRoutes } from './data';
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* AppLayout — Full HTML document for the hotel SaaS app: AppSidebar + AppTopbar + content area.
|
|
4
|
+
* Owns the "Lobby" look through tokens (cobalt accent, slate surfaces) with light and dark palettes.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} title - Page title (product name is appended).
|
|
7
|
+
* @param {string} description - Meta description.
|
|
8
|
+
* @param {string} current - Active route key (dashboard|reservations|rooms|guests|reports|settings).
|
|
9
|
+
* @param {LobbyData} data - Demo/workspace data. Default: Hotel Mirador.
|
|
10
|
+
* @param {SaasRoutes} routes - Page URLs.
|
|
11
|
+
* @param {string} theme - Initial theme (light|dark|auto). The topbar toggle overrides it. Default 'light'.
|
|
12
|
+
* @param {boolean} noindex - Add robots noindex. Default true (app pages are private).
|
|
13
|
+
* @param {string} lang - Document language. Default 'en'.
|
|
14
|
+
*/
|
|
15
|
+
import '../../../../styles/global.css';
|
|
16
|
+
import './lobby-theme.css';
|
|
17
|
+
import AppSidebar from '../../app/AppSidebar.astro';
|
|
18
|
+
import AppTopbar from '../../app/AppTopbar.astro';
|
|
19
|
+
import { lobbyData, saasRoutes, type LobbyData, type SaasRoutes } from './data';
|
|
20
|
+
|
|
21
|
+
interface Props {
|
|
22
|
+
title: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
current?: 'dashboard' | 'reservations' | 'rooms' | 'guests' | 'reports' | 'settings';
|
|
25
|
+
data?: LobbyData;
|
|
26
|
+
routes?: SaasRoutes;
|
|
27
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
28
|
+
noindex?: boolean;
|
|
29
|
+
lang?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const { title, description, current = 'dashboard', data = lobbyData, routes = saasRoutes, theme = 'light', noindex = true, lang = 'en' } = Astro.props;
|
|
33
|
+
|
|
34
|
+
const pending = data.reservations.filter((r) => r.status === 'Pending').length;
|
|
35
|
+
const dirty = data.housekeeping.filter((r) => r.status === 'dirty').length;
|
|
36
|
+
const groups = [
|
|
37
|
+
{ items: [
|
|
38
|
+
{ label: 'Dashboard', href: routes.dashboard, icon: 'dashboard' },
|
|
39
|
+
{ label: 'Reservations', href: routes.reservations, icon: 'calendar', badge: pending || undefined },
|
|
40
|
+
{ label: 'Rooms', href: routes.rooms, icon: 'bed', badge: dirty || undefined },
|
|
41
|
+
{ label: 'Guests', href: routes.guests, icon: 'users' },
|
|
42
|
+
] },
|
|
43
|
+
{ label: 'Insights', items: [{ label: 'Reports', href: routes.reports, icon: 'chart' }] },
|
|
44
|
+
{ label: 'Workspace', items: [{ label: 'Settings', href: routes.settings, icon: 'settings' }] },
|
|
45
|
+
];
|
|
46
|
+
const currentHref = routes[current];
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
<!doctype html>
|
|
50
|
+
<html lang={lang} class="theme-lobby" data-theme={theme === 'auto' ? 'light' : theme}>
|
|
51
|
+
<head>
|
|
52
|
+
<meta charset="utf-8" />
|
|
53
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
54
|
+
<title>{`${title} · ${data.workspace.name} — ${data.product}`}</title>
|
|
55
|
+
{description && <meta name="description" content={description} />}
|
|
56
|
+
{noindex && <meta name="robots" content="noindex, follow" />}
|
|
57
|
+
<script is:inline define:vars={{ initial: theme }}>
|
|
58
|
+
(function () {
|
|
59
|
+
var t = initial;
|
|
60
|
+
try { t = localStorage.getItem('ack-theme') || t; } catch (e) {}
|
|
61
|
+
if (t === 'auto') t = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
62
|
+
document.documentElement.dataset.theme = t;
|
|
63
|
+
})();
|
|
64
|
+
</script>
|
|
65
|
+
</head>
|
|
66
|
+
<body class="lobby">
|
|
67
|
+
<a class="lobby__skip" href="#main">Skip to content</a>
|
|
68
|
+
<div class="lobby__shell">
|
|
69
|
+
<AppSidebar
|
|
70
|
+
workspace={data.workspace}
|
|
71
|
+
groups={groups}
|
|
72
|
+
currentHref={currentHref}
|
|
73
|
+
promo={{ title: 'Channel manager', text: '4 of 5 channels in sync', href: routes.settings, cta: 'Connect Airbnb', progress: 80 }}
|
|
74
|
+
/>
|
|
75
|
+
<div class="lobby__main">
|
|
76
|
+
<AppTopbar notifications={data.notifications} user={data.user} userLinks={[{ label: 'Your profile', href: routes.settings }, { label: 'Team & billing', href: `${routes.settings}#team` }]} signOutHref={routes.signIn} />
|
|
77
|
+
<main id="main" class="lobby__content" tabindex="-1">
|
|
78
|
+
<slot />
|
|
79
|
+
</main>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</body>
|
|
83
|
+
</html>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* DashboardPage — Hotel SaaS overview: KPI tiles, revenue mix, tonight's occupancy gauge,
|
|
4
|
+
* front-desk arrivals/departures, booking channels, occupancy by room type and live activity.
|
|
5
|
+
*
|
|
6
|
+
* Usage (src/pages/app/index.astro):
|
|
7
|
+
* import { DashboardPage } from 'astro-component-kit/templates/saas-hotel';
|
|
8
|
+
* <DashboardPage />
|
|
9
|
+
*
|
|
10
|
+
* @param {LobbyData} data - Workspace data. Default: Hotel Mirador demo.
|
|
11
|
+
* @param {SaasRoutes} routes - Page URLs.
|
|
12
|
+
* @param {string} theme - Initial theme (light|dark|auto). Default 'light'.
|
|
13
|
+
*/
|
|
14
|
+
import AppLayout from './AppLayout.astro';
|
|
15
|
+
import PageHeader from '../../app/PageHeader.astro';
|
|
16
|
+
import ArrivalsBoard from '../../app/ArrivalsBoard.astro';
|
|
17
|
+
import KpiStatTile from '../../charts/KpiStatTile.astro';
|
|
18
|
+
import StackedAreaChart from '../../charts/StackedAreaChart.astro';
|
|
19
|
+
import GaugeChart from '../../charts/GaugeChart.astro';
|
|
20
|
+
import PieChart from '../../charts/PieChart.astro';
|
|
21
|
+
import GroupedBarChart from '../../charts/GroupedBarChart.astro';
|
|
22
|
+
import StatusTimelineFeed from '../../data/StatusTimelineFeed.astro';
|
|
23
|
+
import { lobbyData, saasRoutes, type LobbyData, type SaasRoutes } from './data';
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
data?: LobbyData;
|
|
27
|
+
routes?: SaasRoutes;
|
|
28
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { data = lobbyData, routes = saasRoutes, theme = 'light' } = Astro.props;
|
|
32
|
+
const occupiedTonight = data.housekeeping.filter((r) => r.status === 'occupied').length + data.arrivals.length;
|
|
33
|
+
const tonightPct = Math.min(100, Math.round((occupiedTonight / data.rooms.length) * 100));
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
<AppLayout title="Dashboard" description="Hotel performance at a glance" current="dashboard" data={data} routes={routes} theme={theme}>
|
|
37
|
+
<PageHeader
|
|
38
|
+
title="Dashboard"
|
|
39
|
+
description={`Good morning, ${data.user.name.split(' ')[0]} — here is what is happening at ${data.workspace.name} today.`}
|
|
40
|
+
actions={[{ label: 'Export', icon: 'download' }, { label: 'New booking', icon: 'plus', primary: true, href: routes.reservations }]}
|
|
41
|
+
periods={['Today', '7 days', '30 days', 'Year']}
|
|
42
|
+
activePeriod={2}
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
<div class="lobby-grid lobby-grid--kpis">
|
|
46
|
+
{data.kpis.map((k) => <KpiStatTile label={k.label} value={k.value} change={k.change} invert={k.invert} trend={k.trend} period="vs last month" variant="solid" />)}
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div class="lobby-grid lobby-grid--2-1">
|
|
50
|
+
<StackedAreaChart title="Revenue by department" subtitle="Weekly, in € thousands" categories={data.revenue.categories} series={data.revenue.series} prefix="€" suffix="k" variant="solid" height={260} />
|
|
51
|
+
<GaugeChart prefix="" title="Tonight" subtitle={`${occupiedTonight} of ${data.rooms.length} rooms sold`} value={tonightPct} max={100} label="Occupancy" suffix="%" variant="solid" />
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div class="lobby-grid lobby-grid--1-1">
|
|
55
|
+
<ArrivalsBoard arrivals={data.arrivals} departures={data.departures} inHouse={data.inHouse} />
|
|
56
|
+
<PieChart prefix="" title="Bookings by channel" subtitle="Share of room nights, last 30 days" slices={data.channels} donut centerLabel="Direct 38%" suffix="%" variant="solid" />
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div class="lobby-grid lobby-grid--2-1">
|
|
60
|
+
<GroupedBarChart prefix="" title="Occupancy by room type" subtitle="May – Nov" categories={data.occupancyByType.categories} series={data.occupancyByType.series} suffix="%" variant="solid" height={240} />
|
|
61
|
+
<section class="lobby-card" aria-labelledby="activity-title">
|
|
62
|
+
<h2 id="activity-title" class="lobby-card__title">Live activity</h2>
|
|
63
|
+
<StatusTimelineFeed events={data.activity} />
|
|
64
|
+
</section>
|
|
65
|
+
</div>
|
|
66
|
+
</AppLayout>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* GuestsPage — Guest CRM: searchable guest table (tier, stays, lifetime spend, tags) next to a
|
|
4
|
+
* guest profile with contact, stats, preferences, upcoming stay and staff notes.
|
|
5
|
+
*
|
|
6
|
+
* Usage (src/pages/app/guests.astro):
|
|
7
|
+
* import { GuestsPage } from 'astro-component-kit/templates/saas-hotel';
|
|
8
|
+
* <GuestsPage />
|
|
9
|
+
*
|
|
10
|
+
* @param {LobbyData} data - Workspace data.
|
|
11
|
+
* @param {SaasRoutes} routes - Page URLs.
|
|
12
|
+
* @param {string} theme - Initial theme (light|dark|auto). Default 'light'.
|
|
13
|
+
*/
|
|
14
|
+
import AppLayout from './AppLayout.astro';
|
|
15
|
+
import PageHeader from '../../app/PageHeader.astro';
|
|
16
|
+
import DataTable from '../../app/DataTable.astro';
|
|
17
|
+
import GuestProfileCard from '../../app/GuestProfileCard.astro';
|
|
18
|
+
import { lobbyData, saasRoutes, type LobbyData, type SaasRoutes } from './data';
|
|
19
|
+
|
|
20
|
+
interface Props {
|
|
21
|
+
data?: LobbyData;
|
|
22
|
+
routes?: SaasRoutes;
|
|
23
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { data = lobbyData, routes = saasRoutes, theme = 'light' } = Astro.props;
|
|
27
|
+
const first = data.guests[0];
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<AppLayout title="Guests" current="guests" data={data} routes={routes} theme={theme}>
|
|
31
|
+
<PageHeader
|
|
32
|
+
title="Guests"
|
|
33
|
+
description={`${data.guests.length} profiles · ${data.guests.filter((g) => g.tier === 'Gold' || g.tier === 'Platinum').length} loyalty members`}
|
|
34
|
+
breadcrumb={[{ label: data.workspace.name, href: routes.dashboard }, { label: 'Guests' }]}
|
|
35
|
+
actions={[{ label: 'Export', icon: 'download' }, { label: 'Add guest', icon: 'plus', primary: true }]}
|
|
36
|
+
periods={[]}
|
|
37
|
+
/>
|
|
38
|
+
<div class="lobby-grid lobby-grid--2-1">
|
|
39
|
+
<DataTable
|
|
40
|
+
id="guests"
|
|
41
|
+
title="Guest directory"
|
|
42
|
+
columns={[
|
|
43
|
+
{ key: 'guest', label: 'Guest', type: 'person', sortable: true },
|
|
44
|
+
{ key: 'country', label: 'Country', sortable: true },
|
|
45
|
+
{ key: 'tier', label: 'Tier', type: 'status' },
|
|
46
|
+
{ key: 'stays', label: 'Stays', type: 'number', align: 'right', sortable: true },
|
|
47
|
+
{ key: 'spend', label: 'Lifetime', type: 'money', align: 'right', sortable: true },
|
|
48
|
+
{ key: 'tags', label: 'Tags', type: 'tags' },
|
|
49
|
+
]}
|
|
50
|
+
rows={data.guests}
|
|
51
|
+
statusTones={{ Platinum: 'info', Gold: 'warning', Silver: 'default', Member: 'default' }}
|
|
52
|
+
filterKey="tier"
|
|
53
|
+
bulkActions={['Send offer', 'Export']}
|
|
54
|
+
searchPlaceholder="Search guests…"
|
|
55
|
+
pageSize={8}
|
|
56
|
+
currency={data.currency}
|
|
57
|
+
locale={data.locale}
|
|
58
|
+
/>
|
|
59
|
+
<GuestProfileCard
|
|
60
|
+
guest={{ name: first.guest.name, email: first.guest.sub, phone: '+34 612 345 678', country: first.country, since: 'Mar 2022' }}
|
|
61
|
+
tier={first.tier}
|
|
62
|
+
stats={[{ label: 'Stays', value: String(first.stays) }, { label: 'Nights', value: '19' }, { label: 'Lifetime', value: `€${first.spend.toLocaleString('en-GB')}` }, { label: 'Rating', value: '4.9' }]}
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
</AppLayout>
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* LandingPage — Marketing site for the hotel SaaS: navbar, dashboard hero, customer logos,
|
|
4
|
+
* features, how it works, stats, pricing with monthly/yearly toggle, testimonials, FAQ and footer.
|
|
5
|
+
* Built entirely from existing library sections, themed with the Lobby tokens.
|
|
6
|
+
*
|
|
7
|
+
* Usage (src/pages/index.astro):
|
|
8
|
+
* import { LandingPage } from 'astro-component-kit/templates/saas-hotel';
|
|
9
|
+
* <LandingPage />
|
|
10
|
+
*
|
|
11
|
+
* @param {SaasRoutes} routes - Page URLs.
|
|
12
|
+
* @param {string} theme - Initial theme (light|dark|auto). Default 'light'.
|
|
13
|
+
* @param {boolean} noindex - Add robots noindex. Default false.
|
|
14
|
+
*/
|
|
15
|
+
import '../../../../styles/global.css';
|
|
16
|
+
import './lobby-theme.css';
|
|
17
|
+
import HeroDashboardPreview from '../../sections/HeroDashboardPreview.astro';
|
|
18
|
+
import LogoWallGrid from '../../sections/LogoWallGrid.astro';
|
|
19
|
+
import FeatureIconGrid from '../../sections/FeatureIconGrid.astro';
|
|
20
|
+
import HowItWorksSteps from '../../sections/HowItWorksSteps.astro';
|
|
21
|
+
import StatsCounterBand from '../../sections/StatsCounterBand.astro';
|
|
22
|
+
import PricingToggleCards from '../../sections/PricingToggleCards.astro';
|
|
23
|
+
import TestimonialCarousel from '../../sections/TestimonialCarousel.astro';
|
|
24
|
+
import FaqTwoColumn from '../../sections/FaqTwoColumn.astro';
|
|
25
|
+
import FooterMegaSection from '../../sections/FooterMegaSection.astro';
|
|
26
|
+
import { saasRoutes, type SaasRoutes } from './data';
|
|
27
|
+
|
|
28
|
+
interface Props {
|
|
29
|
+
routes?: SaasRoutes;
|
|
30
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
31
|
+
noindex?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { routes = saasRoutes, theme = 'light', noindex = false } = Astro.props;
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
<!doctype html>
|
|
38
|
+
<html lang="en" class="theme-lobby" data-theme={theme === 'auto' ? 'light' : theme}>
|
|
39
|
+
<head>
|
|
40
|
+
<meta charset="utf-8" />
|
|
41
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
42
|
+
<title>Lobby — the operating system for independent hotels</title>
|
|
43
|
+
<meta name="description" content="Reservations, housekeeping, guest CRM and revenue reports in one calm workspace. Built for independent hotels." />
|
|
44
|
+
{noindex && <meta name="robots" content="noindex, follow" />}
|
|
45
|
+
<script is:inline define:vars={{ initial: theme }}>
|
|
46
|
+
(function () {
|
|
47
|
+
var t = initial;
|
|
48
|
+
try { t = localStorage.getItem('ack-theme') || t; } catch (e) {}
|
|
49
|
+
if (t === 'auto') t = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
50
|
+
document.documentElement.dataset.theme = t;
|
|
51
|
+
})();
|
|
52
|
+
</script>
|
|
53
|
+
</head>
|
|
54
|
+
<body class="lobby lobby--site">
|
|
55
|
+
<a class="lobby__skip" href="#main">Skip to content</a>
|
|
56
|
+
<header class="site-nav">
|
|
57
|
+
<div class="site-nav__inner">
|
|
58
|
+
<a class="site-nav__brand" href={routes.landing}><span aria-hidden="true">L</span>Lobby</a>
|
|
59
|
+
<nav aria-label="Main">
|
|
60
|
+
<ul>
|
|
61
|
+
<li><a href="#features">Product</a></li>
|
|
62
|
+
<li><a href="#how">How it works</a></li>
|
|
63
|
+
<li><a href="#pricing">Pricing</a></li>
|
|
64
|
+
<li><a href="#faq">FAQ</a></li>
|
|
65
|
+
</ul>
|
|
66
|
+
</nav>
|
|
67
|
+
<div class="site-nav__cta">
|
|
68
|
+
<button type="button" class="site-nav__theme" aria-label="Switch theme" data-site-theme>
|
|
69
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="8"/><path d="M12 4a8 8 0 0 1 0 16z" fill="currentColor"/></svg>
|
|
70
|
+
</button>
|
|
71
|
+
<a class="site-nav__link" href={routes.signIn}>Sign in</a>
|
|
72
|
+
<a class="site-nav__btn" href={routes.dashboard}>Open demo</a>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</header>
|
|
76
|
+
|
|
77
|
+
<main id="main" tabindex="-1">
|
|
78
|
+
<HeroDashboardPreview
|
|
79
|
+
badge="New · AI rate suggestions"
|
|
80
|
+
title="The calm way to run an independent hotel"
|
|
81
|
+
subtitle="Reservations, housekeeping, guest profiles and revenue reports in one workspace your whole team will actually enjoy using."
|
|
82
|
+
primary={{ label: 'Start free trial', href: routes.signIn }}
|
|
83
|
+
secondary={{ label: 'Explore the demo', href: routes.dashboard }}
|
|
84
|
+
variant="browser"
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
<LogoWallGrid
|
|
88
|
+
title="Trusted by 2,400+ independent hotels"
|
|
89
|
+
columns={6}
|
|
90
|
+
variant="plain"
|
|
91
|
+
logos={[{ name: 'Casa Mirador' }, { name: 'Hotel Alba' }, { name: 'The Linden' }, { name: 'Nordlys Inn' }, { name: 'Villa Sole' }, { name: 'Harbour House' }]}
|
|
92
|
+
/>
|
|
93
|
+
|
|
94
|
+
<div id="features">
|
|
95
|
+
<FeatureIconGrid
|
|
96
|
+
eyebrow="Everything in one place"
|
|
97
|
+
title="Built for the front desk, loved by the back office"
|
|
98
|
+
subtitle="Replace spreadsheets, sticky notes and three logins with one fast, accessible workspace."
|
|
99
|
+
columns={3}
|
|
100
|
+
features={[
|
|
101
|
+
{ icon: 'layers', title: 'Room rack', text: 'Drag-free tape chart for the next 90 days with live occupancy per night.' },
|
|
102
|
+
{ icon: 'users', title: 'Guest CRM', text: 'Preferences, stays and notes follow every guest — across every channel.' },
|
|
103
|
+
{ icon: 'sparkles', title: 'Housekeeping board', text: 'Clean, dirty, inspecting: rooms update in real time on any device.' },
|
|
104
|
+
{ icon: 'chart', title: 'Revenue reports', text: 'Forecasts, booking pace and channel mix without exporting a single CSV.' },
|
|
105
|
+
{ icon: 'globe', title: 'Channel manager', text: 'Booking.com, Expedia and Airbnb in sync — no more overbookings.' },
|
|
106
|
+
{ icon: 'shield', title: 'Secure by default', text: 'Role-based access, audit log and PCI-compliant payments.' },
|
|
107
|
+
]}
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<div id="how">
|
|
112
|
+
<HowItWorksSteps
|
|
113
|
+
eyebrow="Up and running in a day"
|
|
114
|
+
title="From signup to first check-in"
|
|
115
|
+
steps={[
|
|
116
|
+
{ title: 'Import your rooms', text: 'Upload your room list or connect your current PMS — we map types and rates for you.', time: '10 min' },
|
|
117
|
+
{ title: 'Connect your channels', text: 'Link Booking.com, Expedia and your website booking engine in a few clicks.', time: '20 min' },
|
|
118
|
+
{ title: 'Invite your team', text: 'Give front desk, housekeeping and managers exactly the access they need.', time: '5 min' },
|
|
119
|
+
]}
|
|
120
|
+
cta={{ label: 'Start your free trial', href: routes.signIn }}
|
|
121
|
+
/>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<StatsCounterBand
|
|
125
|
+
title="What hotels see after three months"
|
|
126
|
+
stats={[
|
|
127
|
+
{ value: 9, prefix: '+', suffix: '%', label: 'occupancy' },
|
|
128
|
+
{ value: 38, suffix: '%', label: 'less admin time' },
|
|
129
|
+
{ value: 2.4, prefix: '+€', suffix: 'k', decimals: 1, label: 'direct revenue / month' },
|
|
130
|
+
{ value: 4.9, suffix: '/5', decimals: 1, label: 'support rating' },
|
|
131
|
+
]}
|
|
132
|
+
/>
|
|
133
|
+
|
|
134
|
+
<div id="pricing">
|
|
135
|
+
<PricingToggleCards
|
|
136
|
+
title="Simple pricing, per property"
|
|
137
|
+
subtitle="All plans include unlimited reservations, guest profiles and support in your language."
|
|
138
|
+
currency="€"
|
|
139
|
+
plans={[
|
|
140
|
+
{ name: 'Starter', monthly: 69, yearly: 55, description: 'Up to 15 rooms', features: ['Room rack & reservations', 'Housekeeping board', 'Guest CRM', '2 channels'], cta: 'Start free', href: routes.signIn },
|
|
141
|
+
{ name: 'Pro', monthly: 149, yearly: 119, description: 'Up to 50 rooms', features: ['Everything in Starter', 'Revenue reports', '5 channels', 'Team roles & audit log'], cta: 'Start free trial', href: routes.signIn, featured: true },
|
|
142
|
+
{ name: 'Business', monthly: 299, yearly: 239, description: 'Unlimited rooms', features: ['Everything in Pro', 'Multi-property', 'API & webhooks', 'Dedicated success manager'], cta: 'Talk to sales', href: '#' },
|
|
143
|
+
]}
|
|
144
|
+
/>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<TestimonialCarousel
|
|
148
|
+
title="Hoteliers on Lobby"
|
|
149
|
+
items={[
|
|
150
|
+
{ quote: 'We replaced three tools with Lobby. Check-in takes half the time and occupancy is up 9% this season.', name: 'Lucía Ortega', role: 'GM, Hotel Mirador', rating: 5 },
|
|
151
|
+
{ quote: 'Housekeeping finally knows which rooms to prioritise. No more radio calls.', name: 'Rosa Jiménez', role: 'Head of housekeeping, Casa Alba', rating: 5 },
|
|
152
|
+
{ quote: 'The reports alone paid for the subscription in the first month.', name: 'Daniel Kim', role: 'Revenue manager, The Linden', rating: 5 },
|
|
153
|
+
]}
|
|
154
|
+
/>
|
|
155
|
+
|
|
156
|
+
<div id="faq">
|
|
157
|
+
<FaqTwoColumn
|
|
158
|
+
title="Questions, answered"
|
|
159
|
+
subtitle="Can’t find what you need? Our team replies in under 2 hours."
|
|
160
|
+
support={{ label: 'Contact support', href: '#' }}
|
|
161
|
+
items={[
|
|
162
|
+
{ q: 'Can I migrate from my current PMS?', a: 'Yes. We import rooms, rates, future reservations and guest profiles from the most common systems at no cost.' },
|
|
163
|
+
{ q: 'Do you charge per booking?', a: 'No commissions, ever. You pay a flat monthly price per property.' },
|
|
164
|
+
{ q: 'Does it work on tablets and phones?', a: 'Every screen is responsive and accessible, so housekeeping can update rooms from any device.' },
|
|
165
|
+
{ q: 'Is my data safe?', a: 'Data is encrypted at rest and in transit, hosted in the EU, with daily backups and role-based access.' },
|
|
166
|
+
]}
|
|
167
|
+
/>
|
|
168
|
+
</div>
|
|
169
|
+
</main>
|
|
170
|
+
|
|
171
|
+
<FooterMegaSection
|
|
172
|
+
brand="Lobby"
|
|
173
|
+
tagline="The calm operating system for independent hotels."
|
|
174
|
+
columns={[
|
|
175
|
+
{ title: 'Product', links: [{ label: 'Room rack', href: '#features' }, { label: 'Housekeeping', href: '#features' }, { label: 'Reports', href: '#features' }, { label: 'Pricing', href: '#pricing' }] },
|
|
176
|
+
{ title: 'Company', links: [{ label: 'About', href: '#' }, { label: 'Careers', href: '#' }, { label: 'Contact', href: '#' }] },
|
|
177
|
+
{ title: 'Resources', links: [{ label: 'Help center', href: '#' }, { label: 'API docs', href: '#' }, { label: 'Status', href: '#' }] },
|
|
178
|
+
{ title: 'Legal', links: [{ label: 'Privacy', href: '#' }, { label: 'Terms', href: '#' }, { label: 'DPA', href: '#' }] },
|
|
179
|
+
]}
|
|
180
|
+
copyright={`© ${new Date().getFullYear()} Lobby. Demo template built with Astro Components Kit.`}
|
|
181
|
+
/>
|
|
182
|
+
</body>
|
|
183
|
+
</html>
|
|
184
|
+
|
|
185
|
+
<script>
|
|
186
|
+
document.querySelector('[data-site-theme]')?.addEventListener('click', () => {
|
|
187
|
+
const root = document.documentElement;
|
|
188
|
+
const next = root.dataset.theme === 'light' ? 'dark' : 'light';
|
|
189
|
+
root.dataset.theme = next;
|
|
190
|
+
try { window.localStorage.setItem('ack-theme', next); } catch { /* ignore */ }
|
|
191
|
+
});
|
|
192
|
+
</script>
|
|
193
|
+
|
|
194
|
+
<style is:global>
|
|
195
|
+
.lobby--site main { position: relative; overflow-x: clip; outline: none; }
|
|
196
|
+
.lobby--site main > * , .lobby--site main > div > * { --c-primary: var(--app-accent); --c-primary-light: var(--app-accent); }
|
|
197
|
+
.site-nav { position: sticky; top: 0; z-index: 50; background: rgb(var(--paper-rgb) / 0.82); backdrop-filter: blur(14px) saturate(1.3); border-bottom: 1px solid var(--c-border); }
|
|
198
|
+
.site-nav__inner { display: flex; align-items: center; gap: var(--sp-6); max-width: 1200px; height: 64px; margin: 0 auto; padding: 0 var(--sp-5); }
|
|
199
|
+
.site-nav__brand { display: inline-flex; align-items: center; gap: var(--sp-2); font-weight: 800; font-size: 1.05rem; color: var(--c-text-1); }
|
|
200
|
+
.site-nav__brand span { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 9px; background: var(--app-accent); color: var(--c-on-primary); }
|
|
201
|
+
.site-nav nav ul { display: flex; gap: var(--sp-1); margin: 0; padding: 0; list-style: none; }
|
|
202
|
+
.site-nav nav a { display: inline-flex; align-items: center; min-height: 40px; padding: 0 0.75rem; border-radius: 8px; color: var(--c-text-2); font-size: 0.9rem; font-weight: 500; }
|
|
203
|
+
.site-nav nav a:hover { color: var(--c-text-1); background: rgb(var(--ink-rgb) / 0.05); }
|
|
204
|
+
.site-nav__cta { display: flex; align-items: center; gap: var(--sp-2); margin-left: auto; }
|
|
205
|
+
.site-nav__theme { display: grid; place-items: center; width: 40px; height: 40px; border: 0; border-radius: 10px; background: transparent; color: var(--c-text-2); cursor: pointer; }
|
|
206
|
+
.site-nav__theme:hover { background: rgb(var(--ink-rgb) / 0.06); color: var(--c-text-1); }
|
|
207
|
+
.site-nav__link { min-height: 40px; display: inline-flex; align-items: center; padding: 0 0.75rem; color: var(--c-text-1); font-weight: 600; font-size: 0.9rem; }
|
|
208
|
+
.site-nav__btn { display: inline-flex; align-items: center; min-height: 40px; padding: 0 1rem; border-radius: 10px; background: var(--app-accent); color: var(--c-on-primary); font-weight: 700; font-size: 0.88rem; }
|
|
209
|
+
.site-nav__btn:hover { color: var(--c-on-primary); filter: brightness(1.08); }
|
|
210
|
+
.site-nav :is(a, button):focus-visible { outline: 2px solid var(--app-accent); outline-offset: 2px; }
|
|
211
|
+
@media (max-width: 860px) { .site-nav nav { display: none; } }
|
|
212
|
+
@media (max-width: 420px) { .site-nav__link { display: none; } }
|
|
213
|
+
</style>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* ReportsPage — Revenue management reports: occupancy forecast with confidence band, booking
|
|
4
|
+
* pace heatmap, direct-booking funnel, guest segments, ADR by channel and month-over-month deltas.
|
|
5
|
+
*
|
|
6
|
+
* Usage (src/pages/app/reports.astro):
|
|
7
|
+
* import { ReportsPage } from 'astro-component-kit/templates/saas-hotel';
|
|
8
|
+
* <ReportsPage />
|
|
9
|
+
*
|
|
10
|
+
* @param {LobbyData} data - Workspace data.
|
|
11
|
+
* @param {SaasRoutes} routes - Page URLs.
|
|
12
|
+
* @param {string} theme - Initial theme (light|dark|auto). Default 'light'.
|
|
13
|
+
*/
|
|
14
|
+
import AppLayout from './AppLayout.astro';
|
|
15
|
+
import PageHeader from '../../app/PageHeader.astro';
|
|
16
|
+
import ForecastLineChart from '../../charts/ForecastLineChart.astro';
|
|
17
|
+
import HeatmapChart from '../../charts/HeatmapChart.astro';
|
|
18
|
+
import FunnelChart from '../../charts/FunnelChart.astro';
|
|
19
|
+
import SegmentedBarChart from '../../charts/SegmentedBarChart.astro';
|
|
20
|
+
import HorizontalBarChart from '../../charts/HorizontalBarChart.astro';
|
|
21
|
+
import MetricDeltaTable from '../../charts/MetricDeltaTable.astro';
|
|
22
|
+
import { lobbyData, saasRoutes, type LobbyData, type SaasRoutes } from './data';
|
|
23
|
+
|
|
24
|
+
interface Props {
|
|
25
|
+
data?: LobbyData;
|
|
26
|
+
routes?: SaasRoutes;
|
|
27
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { data = lobbyData, routes = saasRoutes, theme = 'light' } = Astro.props;
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
<AppLayout title="Reports" current="reports" data={data} routes={routes} theme={theme}>
|
|
34
|
+
<PageHeader
|
|
35
|
+
title="Reports"
|
|
36
|
+
description="Revenue, pace and conversion — updated every hour."
|
|
37
|
+
breadcrumb={[{ label: data.workspace.name, href: routes.dashboard }, { label: 'Reports' }]}
|
|
38
|
+
actions={[{ label: 'Schedule', icon: 'calendar' }, { label: 'Download PDF', icon: 'download', primary: true }]}
|
|
39
|
+
periods={['30 days', 'Quarter', 'Year']}
|
|
40
|
+
activePeriod={0}
|
|
41
|
+
/>
|
|
42
|
+
<ForecastLineChart prefix="" title="Occupancy forecast" subtitle="Actual vs. forecast with 90% confidence" categories={data.forecast.categories} actual={data.forecast.actual} forecast={data.forecast.forecast} suffix="%" variant="solid" height={260} />
|
|
43
|
+
<div class="lobby-grid lobby-grid--1-1">
|
|
44
|
+
<HeatmapChart title="Booking pace" subtitle="Rooms booked by arrival weekday and lead time" rows={data.pace.rows} columns={data.pace.columns} values={data.pace.values} showValues variant="solid" />
|
|
45
|
+
<FunnelChart title="Direct booking funnel" subtitle="Website, last 30 days" stages={data.funnel} variant="solid" />
|
|
46
|
+
</div>
|
|
47
|
+
<div class="lobby-grid lobby-grid--1-1">
|
|
48
|
+
<SegmentedBarChart title="Guest segments" segments={data.segments} total="Room nights" suffix="%" variant="solid" />
|
|
49
|
+
<HorizontalBarChart title="ADR by channel" items={[{ label: 'Direct', value: 182 }, { label: 'Corporate', value: 171 }, { label: 'Booking.com', value: 164 }, { label: 'Expedia', value: 158 }, { label: 'Airbnb', value: 149 }]} highlight="Direct" prefix="€" variant="solid" />
|
|
50
|
+
</div>
|
|
51
|
+
<MetricDeltaTable
|
|
52
|
+
title="Month over month"
|
|
53
|
+
currentLabel="October"
|
|
54
|
+
previousLabel="September"
|
|
55
|
+
rows={[
|
|
56
|
+
{ label: 'Occupancy (%)', current: 84, previous: 79, trend: [70, 74, 79, 84] },
|
|
57
|
+
{ label: 'ADR (€)', current: 168, previous: 162, trend: [150, 158, 162, 168] },
|
|
58
|
+
{ label: 'RevPAR (€)', current: 141, previous: 128, trend: [105, 117, 128, 141] },
|
|
59
|
+
{ label: 'Cancellation rate (%)', current: 3.1, previous: 4.2, invert: true, trend: [5.1, 4.8, 4.2, 3.1] },
|
|
60
|
+
]}
|
|
61
|
+
variant="solid"
|
|
62
|
+
/>
|
|
63
|
+
</AppLayout>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* ReservationsPage — Room rack (tape chart) for the next two weeks plus a searchable, sortable
|
|
4
|
+
* reservations table with status filters, bulk actions and pagination.
|
|
5
|
+
*
|
|
6
|
+
* Usage (src/pages/app/reservations.astro):
|
|
7
|
+
* import { ReservationsPage } from 'astro-component-kit/templates/saas-hotel';
|
|
8
|
+
* <ReservationsPage />
|
|
9
|
+
*
|
|
10
|
+
* @param {LobbyData} data - Workspace data.
|
|
11
|
+
* @param {SaasRoutes} routes - Page URLs.
|
|
12
|
+
* @param {string} theme - Initial theme (light|dark|auto). Default 'light'.
|
|
13
|
+
*/
|
|
14
|
+
import AppLayout from './AppLayout.astro';
|
|
15
|
+
import PageHeader from '../../app/PageHeader.astro';
|
|
16
|
+
import RoomRack from '../../app/RoomRack.astro';
|
|
17
|
+
import DataTable from '../../app/DataTable.astro';
|
|
18
|
+
import { lobbyData, saasRoutes, type LobbyData, type SaasRoutes } from './data';
|
|
19
|
+
|
|
20
|
+
interface Props {
|
|
21
|
+
data?: LobbyData;
|
|
22
|
+
routes?: SaasRoutes;
|
|
23
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { data = lobbyData, routes = saasRoutes, theme = 'light' } = Astro.props;
|
|
27
|
+
const count = (s: string) => data.reservations.filter((r) => r.status === s).length;
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<AppLayout title="Reservations" current="reservations" data={data} routes={routes} theme={theme}>
|
|
31
|
+
<PageHeader
|
|
32
|
+
title="Reservations"
|
|
33
|
+
description={`${data.reservations.length} bookings in the next 14 days · ${count('Pending')} waiting for confirmation`}
|
|
34
|
+
breadcrumb={[{ label: data.workspace.name, href: routes.dashboard }, { label: 'Reservations' }]}
|
|
35
|
+
actions={[{ label: 'Import', icon: 'download' }, { label: 'New booking', icon: 'plus', primary: true }]}
|
|
36
|
+
periods={[]}
|
|
37
|
+
/>
|
|
38
|
+
<RoomRack days={data.days} today={1} rooms={data.rooms} bookings={data.bookings as any} />
|
|
39
|
+
<DataTable
|
|
40
|
+
id="reservations"
|
|
41
|
+
title="All reservations"
|
|
42
|
+
rows={data.reservations}
|
|
43
|
+
searchPlaceholder="Search guest, booking or room…"
|
|
44
|
+
bulkActions={['Send confirmation', 'Export CSV', 'Assign rooms']}
|
|
45
|
+
pageSize={8}
|
|
46
|
+
currency={data.currency}
|
|
47
|
+
locale={data.locale}
|
|
48
|
+
/>
|
|
49
|
+
</AppLayout>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* RoomsPage — Housekeeping and room status: status KPI tiles, inventory by room type and the
|
|
4
|
+
* interactive HousekeepingBoard grouped by floor.
|
|
5
|
+
*
|
|
6
|
+
* Usage (src/pages/app/rooms.astro):
|
|
7
|
+
* import { RoomsPage } from 'astro-component-kit/templates/saas-hotel';
|
|
8
|
+
* <RoomsPage />
|
|
9
|
+
*
|
|
10
|
+
* @param {LobbyData} data - Workspace data.
|
|
11
|
+
* @param {SaasRoutes} routes - Page URLs.
|
|
12
|
+
* @param {string} theme - Initial theme (light|dark|auto). Default 'light'.
|
|
13
|
+
*/
|
|
14
|
+
import AppLayout from './AppLayout.astro';
|
|
15
|
+
import PageHeader from '../../app/PageHeader.astro';
|
|
16
|
+
import HousekeepingBoard from '../../app/HousekeepingBoard.astro';
|
|
17
|
+
import SegmentedBarChart from '../../charts/SegmentedBarChart.astro';
|
|
18
|
+
import KpiStatTile from '../../charts/KpiStatTile.astro';
|
|
19
|
+
import { lobbyData, saasRoutes, type LobbyData, type SaasRoutes } from './data';
|
|
20
|
+
|
|
21
|
+
interface Props {
|
|
22
|
+
data?: LobbyData;
|
|
23
|
+
routes?: SaasRoutes;
|
|
24
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const { data = lobbyData, routes = saasRoutes, theme = 'light' } = Astro.props;
|
|
28
|
+
const by = (s: string) => data.housekeeping.filter((r) => r.status === s).length;
|
|
29
|
+
const types = Array.from(new Set(data.rooms.map((r) => r.type))).map((t) => ({ label: t, value: data.rooms.filter((r) => r.type === t).length }));
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
<AppLayout title="Rooms" current="rooms" data={data} routes={routes} theme={theme}>
|
|
33
|
+
<PageHeader
|
|
34
|
+
title="Rooms & housekeeping"
|
|
35
|
+
description="Tap a dirty room to move it to inspection, then to clean."
|
|
36
|
+
breadcrumb={[{ label: data.workspace.name, href: routes.dashboard }, { label: 'Rooms' }]}
|
|
37
|
+
actions={[{ label: 'Assign tasks', icon: 'broom' }, { label: 'Block room', icon: 'door', primary: true }]}
|
|
38
|
+
periods={[]}
|
|
39
|
+
/>
|
|
40
|
+
<div class="lobby-grid lobby-grid--kpis">
|
|
41
|
+
<KpiStatTile label="Ready to sell" value={String(by('clean'))} change={12} period="vs yesterday" variant="solid" size="sm" />
|
|
42
|
+
<KpiStatTile label="To clean" value={String(by('dirty'))} change={-8} invert period="vs yesterday" variant="solid" size="sm" />
|
|
43
|
+
<KpiStatTile label="Occupied" value={String(by('occupied'))} change={4} period="vs yesterday" variant="solid" size="sm" />
|
|
44
|
+
<KpiStatTile label="Out of order" value={String(by('ooo'))} change={0} invert period="vs yesterday" variant="solid" size="sm" />
|
|
45
|
+
</div>
|
|
46
|
+
<SegmentedBarChart title="Inventory by room type" segments={types} total={`${data.rooms.length} rooms`} suffix=" rooms" variant="solid" />
|
|
47
|
+
<HousekeepingBoard rooms={data.housekeeping} title="Room board" />
|
|
48
|
+
</AppLayout>
|