@trackany-device/components 1.0.0
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/package.json +185 -0
- package/src/assets/logo.png +0 -0
- package/src/assets/map/arrows/map-arrow-blue.png +0 -0
- package/src/assets/map/arrows/map-arrow-green.png +0 -0
- package/src/assets/map/arrows/map-arrow-purple.png +0 -0
- package/src/assets/map/arrows/map-arrow-red.png +0 -0
- package/src/assets/map/flags/flag-blue.png +0 -0
- package/src/assets/map/flags/flag-green.png +0 -0
- package/src/assets/map/flags/flag-red.png +0 -0
- package/src/assets/map/flags/flag-yellow.png +0 -0
- package/src/assets/map/pins/map-pin-blue.png +0 -0
- package/src/assets/map/pins/map-pin-green.png +0 -0
- package/src/assets/map/pins/map-pin-purple.png +0 -0
- package/src/assets/map/pins/map-pin-red.png +0 -0
- package/src/components/Card.tsx +9 -0
- package/src/components/alert-error.tsx +24 -0
- package/src/components/app-content.tsx +22 -0
- package/src/components/app-header.tsx +153 -0
- package/src/components/app-logo-icon.tsx +13 -0
- package/src/components/app-logo.tsx +21 -0
- package/src/components/app-shell.tsx +19 -0
- package/src/components/app-sidebar-header.tsx +68 -0
- package/src/components/app-sidebar.tsx +106 -0
- package/src/components/appearance-tabs.tsx +46 -0
- package/src/components/breadcrumbs.tsx +50 -0
- package/src/components/cms/blurred-image.tsx +111 -0
- package/src/components/cms/section-bg.tsx +473 -0
- package/src/components/cms/section-button.tsx +127 -0
- package/src/components/cms/sections/banner-5050-section.tsx +135 -0
- package/src/components/cms/sections/blogs-listing-section.tsx +270 -0
- package/src/components/cms/sections/cards-grid-section.tsx +185 -0
- package/src/components/cms/sections/contact-form-section.tsx +157 -0
- package/src/components/cms/sections/cta-section.tsx +101 -0
- package/src/components/cms/sections/featured-blog-slider-section.tsx +256 -0
- package/src/components/cms/sections/featured-products-grid-section.tsx +173 -0
- package/src/components/cms/sections/featured-solutions-grid-section.tsx +183 -0
- package/src/components/cms/sections/hero-section.tsx +180 -0
- package/src/components/cms/sections/solutions-with-filter-section.tsx +234 -0
- package/src/components/cms/sections/text-section.tsx +77 -0
- package/src/components/cutout-image.tsx +228 -0
- package/src/components/devices/devices-mini-map.tsx +275 -0
- package/src/components/docs/docs-shell.tsx +280 -0
- package/src/components/fleet-hero-animated.tsx +383 -0
- package/src/components/input-error.tsx +17 -0
- package/src/components/keenicons/assets/duotone/Read Me.txt +7 -0
- package/src/components/keenicons/assets/duotone/demo-files/demo.css +160 -0
- package/src/components/keenicons/assets/duotone/demo-files/demo.js +32 -0
- package/src/components/keenicons/assets/duotone/demo.html +12424 -0
- package/src/components/keenicons/assets/duotone/fonts/keenicons-duotone.svg +1109 -0
- package/src/components/keenicons/assets/duotone/fonts/keenicons-duotone.ttf +0 -0
- package/src/components/keenicons/assets/duotone/fonts/keenicons-duotone.woff +0 -0
- package/src/components/keenicons/assets/duotone/selection.json +17313 -0
- package/src/components/keenicons/assets/duotone/style.css +4931 -0
- package/src/components/keenicons/assets/filled/Read Me.txt +7 -0
- package/src/components/keenicons/assets/filled/demo-files/demo.css +160 -0
- package/src/components/keenicons/assets/filled/demo-files/demo.js +32 -0
- package/src/components/keenicons/assets/filled/demo.html +12370 -0
- package/src/components/keenicons/assets/filled/fonts/keenicons-filled.svg +1082 -0
- package/src/components/keenicons/assets/filled/fonts/keenicons-filled.ttf +0 -0
- package/src/components/keenicons/assets/filled/fonts/keenicons-filled.woff +0 -0
- package/src/components/keenicons/assets/filled/selection.json +17096 -0
- package/src/components/keenicons/assets/filled/style.css +4769 -0
- package/src/components/keenicons/assets/outline/Read Me.txt +7 -0
- package/src/components/keenicons/assets/outline/demo-files/demo.css +160 -0
- package/src/components/keenicons/assets/outline/demo-files/demo.js +32 -0
- package/src/components/keenicons/assets/outline/demo.html +11356 -0
- package/src/components/keenicons/assets/outline/fonts/keenicons-outline.svg +575 -0
- package/src/components/keenicons/assets/outline/fonts/keenicons-outline.ttf +0 -0
- package/src/components/keenicons/assets/outline/fonts/keenicons-outline.woff +0 -0
- package/src/components/keenicons/assets/outline/selection.json +13054 -0
- package/src/components/keenicons/assets/outline/style.css +1721 -0
- package/src/components/keenicons/assets/solid/Read Me.txt +7 -0
- package/src/components/keenicons/assets/solid/demo-files/demo.css +160 -0
- package/src/components/keenicons/assets/solid/demo-files/demo.js +32 -0
- package/src/components/keenicons/assets/solid/demo.html +11356 -0
- package/src/components/keenicons/assets/solid/fonts/keenicons-solid.svg +575 -0
- package/src/components/keenicons/assets/solid/fonts/keenicons-solid.ttf +0 -0
- package/src/components/keenicons/assets/solid/fonts/keenicons-solid.woff +0 -0
- package/src/components/keenicons/assets/solid/selection.json +13048 -0
- package/src/components/keenicons/assets/solid/style.css +1721 -0
- package/src/components/keenicons/assets/styles.css +4 -0
- package/src/components/keenicons/index.ts +2 -0
- package/src/components/keenicons/keenicons.tsx +16 -0
- package/src/components/keenicons/types.ts +7 -0
- package/src/components/nav-footer.tsx +49 -0
- package/src/components/nav-main.tsx +53 -0
- package/src/components/nav-user.tsx +59 -0
- package/src/components/notification-bell.tsx +190 -0
- package/src/components/products/product-card.tsx +159 -0
- package/src/components/text-link.tsx +23 -0
- package/src/components/ui/accordion-menu.tsx +322 -0
- package/src/components/ui/accordion.tsx +133 -0
- package/src/components/ui/alert-dialog.tsx +82 -0
- package/src/components/ui/alert.tsx +63 -0
- package/src/components/ui/avatar-group.tsx +129 -0
- package/src/components/ui/avatar.tsx +67 -0
- package/src/components/ui/badge.tsx +230 -0
- package/src/components/ui/breadcrumb.tsx +88 -0
- package/src/components/ui/button.tsx +412 -0
- package/src/components/ui/calendar.tsx +56 -0
- package/src/components/ui/card.tsx +147 -0
- package/src/components/ui/chart.tsx +290 -0
- package/src/components/ui/checkbox.tsx +47 -0
- package/src/components/ui/code.tsx +45 -0
- package/src/components/ui/collapsible.tsx +31 -0
- package/src/components/ui/command-palette.tsx +189 -0
- package/src/components/ui/command.tsx +138 -0
- package/src/components/ui/cookie-banner.tsx +220 -0
- package/src/components/ui/copy-button.tsx +60 -0
- package/src/components/ui/data-grid-column-filter.tsx +124 -0
- package/src/components/ui/data-grid-column-header.tsx +284 -0
- package/src/components/ui/data-grid-column-visibility.tsx +38 -0
- package/src/components/ui/data-grid-pagination.tsx +206 -0
- package/src/components/ui/data-grid-table-dnd-rows.tsx +147 -0
- package/src/components/ui/data-grid-table-dnd.tsx +175 -0
- package/src/components/ui/data-grid-table.tsx +500 -0
- package/src/components/ui/data-grid.tsx +193 -0
- package/src/components/ui/data-list.tsx +76 -0
- package/src/components/ui/datefield.tsx +91 -0
- package/src/components/ui/dialog.tsx +139 -0
- package/src/components/ui/divider.tsx +41 -0
- package/src/components/ui/drawer.tsx +59 -0
- package/src/components/ui/dropdown-menu.tsx +224 -0
- package/src/components/ui/empty-state.tsx +54 -0
- package/src/components/ui/file-upload.tsx +152 -0
- package/src/components/ui/form.tsx +88 -0
- package/src/components/ui/icon.tsx +14 -0
- package/src/components/ui/input-otp.tsx +71 -0
- package/src/components/ui/input.tsx +155 -0
- package/src/components/ui/kbd.tsx +26 -0
- package/src/components/ui/label.tsx +31 -0
- package/src/components/ui/navigation-menu.tsx +168 -0
- package/src/components/ui/pagination.tsx +37 -0
- package/src/components/ui/placeholder-pattern.tsx +21 -0
- package/src/components/ui/popover.tsx +50 -0
- package/src/components/ui/progress.tsx +65 -0
- package/src/components/ui/radio-group.tsx +73 -0
- package/src/components/ui/resizable.tsx +39 -0
- package/src/components/ui/scroll-area.tsx +50 -0
- package/src/components/ui/select.tsx +234 -0
- package/src/components/ui/separator.tsx +24 -0
- package/src/components/ui/sheet.tsx +147 -0
- package/src/components/ui/sidebar.tsx +721 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/ui/slider.tsx +35 -0
- package/src/components/ui/sonner.tsx +28 -0
- package/src/components/ui/sortable.tsx +724 -0
- package/src/components/ui/spinner.tsx +17 -0
- package/src/components/ui/stat-card.tsx +82 -0
- package/src/components/ui/stepper.tsx +410 -0
- package/src/components/ui/switch.tsx +68 -0
- package/src/components/ui/table.tsx +42 -0
- package/src/components/ui/tabs.tsx +196 -0
- package/src/components/ui/timeline.tsx +90 -0
- package/src/components/ui/toggle-group.tsx +73 -0
- package/src/components/ui/toggle.tsx +45 -0
- package/src/components/ui/tooltip.tsx +55 -0
- package/src/components/user-info.tsx +33 -0
- package/src/components/user-menu-content.tsx +53 -0
- package/src/components/web/SiteFooter.tsx +154 -0
- package/src/components/web/SiteHeader.tsx +159 -0
- package/src/components/workflows/workflow-canvas.tsx +321 -0
- package/src/controls/Blockquote.tsx +25 -0
- package/src/controls/Button.tsx +101 -0
- package/src/controls/Checkbox.tsx +29 -0
- package/src/controls/DateField.tsx +37 -0
- package/src/controls/FormField.tsx +20 -0
- package/src/controls/Heading.tsx +28 -0
- package/src/controls/Input.tsx +21 -0
- package/src/controls/Label.tsx +18 -0
- package/src/controls/Paragraph.tsx +39 -0
- package/src/controls/PasswordInput.tsx +40 -0
- package/src/controls/RadioGroup.tsx +70 -0
- package/src/controls/Select.tsx +24 -0
- package/src/controls/Slider.tsx +33 -0
- package/src/controls/Switch.tsx +31 -0
- package/src/controls/Textarea.tsx +22 -0
- package/src/elements/ConfirmPasswordForm.tsx +43 -0
- package/src/elements/DeviceStatusBadge.tsx +38 -0
- package/src/elements/DriverCard.tsx +67 -0
- package/src/elements/ForgotPasswordForm.tsx +64 -0
- package/src/elements/IncidentCard.tsx +67 -0
- package/src/elements/LoginForm.tsx +100 -0
- package/src/elements/OtpForm.tsx +71 -0
- package/src/elements/RegisterForm.tsx +150 -0
- package/src/elements/ResetPasswordForm.tsx +72 -0
- package/src/elements/SmsChallengeForm.tsx +104 -0
- package/src/elements/VehicleCard.tsx +73 -0
- package/src/elements/VerifyEmailForm.tsx +39 -0
- package/src/hooks/use-appearance.tsx +117 -0
- package/src/hooks/use-applied-theme.ts +98 -0
- package/src/hooks/use-clipboard.ts +34 -0
- package/src/hooks/use-current-url.ts +83 -0
- package/src/hooks/use-dark-mode.ts +48 -0
- package/src/hooks/use-flash-toast.ts +29 -0
- package/src/hooks/use-initials.tsx +24 -0
- package/src/hooks/use-mobile-navigation.ts +12 -0
- package/src/hooks/use-mobile.tsx +38 -0
- package/src/index.ts +408 -0
- package/src/layouts/AppLayout.tsx +60 -0
- package/src/layouts/AuthLayout.tsx +32 -0
- package/src/layouts/SettingsLayout.tsx +21 -0
- package/src/layouts/app/AIChatLayout.tsx +73 -0
- package/src/layouts/app/AsideSidebarLayout.tsx +3 -0
- package/src/layouts/app/CalendarSidebarLayout.tsx +69 -0
- package/src/layouts/app/CommunitiesNavbarLayout.tsx +3 -0
- package/src/layouts/app/DualNavbarSidebarLayout.tsx +3 -0
- package/src/layouts/app/FocusSidebarLayout.tsx +75 -0
- package/src/layouts/app/MailLayout.tsx +69 -0
- package/src/layouts/app/MegaMenuHeaderLayout.tsx +3 -0
- package/src/layouts/app/MegaMenuLayout.tsx +81 -0
- package/src/layouts/app/MegaMenuNavbarLayout.tsx +88 -0
- package/src/layouts/app/MegaMenuSearchNavbarLayout.tsx +3 -0
- package/src/layouts/app/NavbarCollapsibleLayout.tsx +88 -0
- package/src/layouts/app/NavbarCollapsibleLinksLayout.tsx +3 -0
- package/src/layouts/app/NavbarMinimalLayout.tsx +3 -0
- package/src/layouts/app/NavbarMinimalSidebarLayout.tsx +3 -0
- package/src/layouts/app/NavbarSidebarDashboardLayout.tsx +3 -0
- package/src/layouts/app/NavbarSidebarLayout.tsx +92 -0
- package/src/layouts/app/NavbarSimpleSidebarLayout.tsx +3 -0
- package/src/layouts/app/NavbarTitledSidebarLayout.tsx +3 -0
- package/src/layouts/app/PanelSidebarLayout.tsx +3 -0
- package/src/layouts/app/SearchNavbarSidebarLayout.tsx +3 -0
- package/src/layouts/app/SidebarBreadcrumbLayout.tsx +3 -0
- package/src/layouts/app/SidebarCleanLayout.tsx +3 -0
- package/src/layouts/app/SidebarCommunitiesLayout.tsx +3 -0
- package/src/layouts/app/SidebarContentLayout.tsx +3 -0
- package/src/layouts/app/SidebarDualMenuLayout.tsx +104 -0
- package/src/layouts/app/SidebarFixedLayout.tsx +166 -0
- package/src/layouts/app/SidebarFooterNavbarLayout.tsx +3 -0
- package/src/layouts/app/SidebarHeaderMenuLayout.tsx +3 -0
- package/src/layouts/app/SidebarMegaMenuLayout.tsx +4 -0
- package/src/layouts/app/SidebarMinimalLayout.tsx +70 -0
- package/src/layouts/app/SidebarMobileSearchLayout.tsx +3 -0
- package/src/layouts/app/SidebarMultiPanelLayout.tsx +3 -0
- package/src/layouts/app/SidebarPrimarySecondaryLayout.tsx +3 -0
- package/src/layouts/app/SidebarSearchHeaderLayout.tsx +103 -0
- package/src/layouts/app/SidebarSearchToolbarLayout.tsx +3 -0
- package/src/layouts/app/SidebarTabsDualLayout.tsx +3 -0
- package/src/layouts/app/SidebarTabsLayout.tsx +98 -0
- package/src/layouts/app/SidebarTreeLayout.tsx +3 -0
- package/src/layouts/app/SplitNavbarLayout.tsx +3 -0
- package/src/layouts/app/SplitSidebarDashboardLayout.tsx +3 -0
- package/src/layouts/app/SplitSidebarLayout.tsx +99 -0
- package/src/layouts/app/TopNavLayout.tsx +105 -0
- package/src/layouts/app/TopNavLinksLayout.tsx +3 -0
- package/src/layouts/app/WorkspaceBreadcrumbLayout.tsx +3 -0
- package/src/layouts/app/WorkspaceCommunitiesLayout.tsx +3 -0
- package/src/layouts/app/WorkspaceNavbarLayout.tsx +3 -0
- package/src/layouts/app/WorkspaceSidebarLayout.tsx +98 -0
- package/src/layouts/app/WorkspaceSidebarTitleLayout.tsx +3 -0
- package/src/layouts/app/app-header-layout.tsx +45 -0
- package/src/layouts/app/app-sidebar-layout.tsx +56 -0
- package/src/layouts/app/layout-context.tsx +44 -0
- package/src/layouts/app/layout-types.ts +47 -0
- package/src/layouts/app/partials/Footer.tsx +35 -0
- package/src/layouts/app/partials/HeaderTopbar.tsx +96 -0
- package/src/layouts/app/partials/Navbar.tsx +85 -0
- package/src/layouts/app/partials/Toolbar.tsx +47 -0
- package/src/layouts/app-layout.tsx +29 -0
- package/src/layouts/auth/AuthBrandedLayout.tsx +58 -0
- package/src/layouts/auth/AuthCardLayout.tsx +31 -0
- package/src/layouts/auth/AuthCenteredLayout.tsx +41 -0
- package/src/layouts/auth/AuthClassicLayout.tsx +41 -0
- package/src/layouts/auth/AuthSimpleLayout.tsx +33 -0
- package/src/layouts/auth/AuthSplitLayout.tsx +89 -0
- package/src/layouts/web-app-layout.tsx +162 -0
- package/src/layouts/web-layout.tsx +23 -0
- package/src/lib/datetime.ts +188 -0
- package/src/lib/google-maps-loader.ts +99 -0
- package/src/lib/location.ts +127 -0
- package/src/lib/lucide-icon-map.ts +132 -0
- package/src/lib/map-markers.ts +124 -0
- package/src/lib/map-styles.ts +351 -0
- package/src/lib/utils.ts +11 -0
- package/src/platform/adapters/default.tsx +156 -0
- package/src/platform/adapters/inertia.tsx +88 -0
- package/src/platform/adapters/nextjs.ts +86 -0
- package/src/platform/context.tsx +106 -0
- package/src/platform/index.ts +27 -0
- package/src/platform/types.ts +105 -0
- package/src/styles/layouts/sidebar-fixed.css +161 -0
- package/src/styles/themes.css +583 -0
- package/src/types/assets.d.ts +5 -0
- package/src/types/auth.ts +25 -0
- package/src/types/global.d.ts +13 -0
- package/src/types/index.ts +9 -0
- package/src/types/navigation.ts +15 -0
- package/src/types/ui.ts +32 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { PlatformLink } from '../../platform/context';
|
|
2
|
+
/**
|
|
3
|
+
* DocsShell — reusable docs layout used by every doc series.
|
|
4
|
+
*
|
|
5
|
+
* Matches the TAD101 docs visual language (sidebar with section list,
|
|
6
|
+
* prose article in the main column, prev/next pager at the foot). Each
|
|
7
|
+
* doc series passes its own sections + active id; the shell takes care
|
|
8
|
+
* of the chrome.
|
|
9
|
+
*
|
|
10
|
+
* Existing TAD101 pages keep their dedicated DocsLayout (which is a
|
|
11
|
+
* specialised wrapper around this shell) so changes here can't break
|
|
12
|
+
* those pages until they're migrated.
|
|
13
|
+
*/
|
|
14
|
+
import {
|
|
15
|
+
AlertTriangle,
|
|
16
|
+
ArrowLeft,
|
|
17
|
+
ArrowRight,
|
|
18
|
+
CheckCircle2,
|
|
19
|
+
Info,
|
|
20
|
+
} from 'lucide-react';
|
|
21
|
+
import type { LucideIcon } from 'lucide-react';
|
|
22
|
+
|
|
23
|
+
import { Button } from '../ui/button';
|
|
24
|
+
import { Card } from '../ui/card';
|
|
25
|
+
import { CopyButton } from '../ui/copy-button';
|
|
26
|
+
import { cn } from '../../lib/utils';
|
|
27
|
+
|
|
28
|
+
export type DocsSection<TId extends string = string> = {
|
|
29
|
+
id: TId;
|
|
30
|
+
title: string;
|
|
31
|
+
href: string;
|
|
32
|
+
icon: LucideIcon;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type Props<TId extends string> = {
|
|
36
|
+
/** Short brand label rendered in the sidebar header (e.g. "TAD101", "Tenant Manual"). */
|
|
37
|
+
seriesLabel: string;
|
|
38
|
+
/** Optional version pill shown next to the series label. */
|
|
39
|
+
version?: string;
|
|
40
|
+
/** Optional last-updated date string shown under the sidebar nav. */
|
|
41
|
+
lastUpdated?: string;
|
|
42
|
+
/** All sections in this doc series. */
|
|
43
|
+
sections: DocsSection<TId>[];
|
|
44
|
+
/** Which section the current page represents. */
|
|
45
|
+
active: TId;
|
|
46
|
+
/** Page title rendered as the h1 + browser title. */
|
|
47
|
+
pageTitle: string;
|
|
48
|
+
/** Optional eyebrow above the h1 (defaults to seriesLabel). */
|
|
49
|
+
eyebrow?: string;
|
|
50
|
+
children: React.ReactNode;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default function DocsShell<TId extends string>({
|
|
54
|
+
seriesLabel,
|
|
55
|
+
version,
|
|
56
|
+
lastUpdated,
|
|
57
|
+
sections,
|
|
58
|
+
active,
|
|
59
|
+
pageTitle,
|
|
60
|
+
eyebrow,
|
|
61
|
+
children,
|
|
62
|
+
}: Props<TId>) {
|
|
63
|
+
return (
|
|
64
|
+
<>
|
|
65
|
+
|
|
66
|
+
<div className="mx-auto flex w-full max-w-7xl gap-6 px-4 py-10 lg:gap-10 lg:px-8">
|
|
67
|
+
<aside className="hidden w-64 shrink-0 lg:block">
|
|
68
|
+
<Card className="sticky top-24 border-border bg-card p-4">
|
|
69
|
+
<div className="mb-3 flex items-center justify-between">
|
|
70
|
+
<PlatformLink
|
|
71
|
+
href="/docs"
|
|
72
|
+
className="text-xs font-semibold tracking-widest text-muted-foreground uppercase hover:text-foreground"
|
|
73
|
+
>
|
|
74
|
+
{seriesLabel}
|
|
75
|
+
</PlatformLink>
|
|
76
|
+
{version && (
|
|
77
|
+
<span className="rounded-full bg-primary/10 px-2 py-0.5 font-mono text-[10px] font-medium text-primary">
|
|
78
|
+
v{version}
|
|
79
|
+
</span>
|
|
80
|
+
)}
|
|
81
|
+
</div>
|
|
82
|
+
<nav className="space-y-0.5">
|
|
83
|
+
{sections.map((s) => {
|
|
84
|
+
const Icon = s.icon;
|
|
85
|
+
const isActive = s.id === active;
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<PlatformLink
|
|
89
|
+
key={s.id}
|
|
90
|
+
href={s.href}
|
|
91
|
+
className={cn(
|
|
92
|
+
'flex items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors',
|
|
93
|
+
isActive
|
|
94
|
+
? 'bg-primary/10 font-semibold text-primary'
|
|
95
|
+
: 'text-muted-foreground hover:bg-accent hover:text-accent-foreground',
|
|
96
|
+
)}
|
|
97
|
+
>
|
|
98
|
+
<Icon
|
|
99
|
+
className={cn(
|
|
100
|
+
'size-4 shrink-0',
|
|
101
|
+
isActive
|
|
102
|
+
? 'text-primary'
|
|
103
|
+
: 'text-muted-foreground/70',
|
|
104
|
+
)}
|
|
105
|
+
/>
|
|
106
|
+
<span>{s.title}</span>
|
|
107
|
+
</PlatformLink>
|
|
108
|
+
);
|
|
109
|
+
})}
|
|
110
|
+
</nav>
|
|
111
|
+
{lastUpdated && (
|
|
112
|
+
<div className="mt-4 border-t border-border pt-3 text-xs text-muted-foreground">
|
|
113
|
+
Last updated:{' '}
|
|
114
|
+
<span className="text-foreground">
|
|
115
|
+
{lastUpdated}
|
|
116
|
+
</span>
|
|
117
|
+
</div>
|
|
118
|
+
)}
|
|
119
|
+
<div className="mt-3 border-t border-border pt-3">
|
|
120
|
+
<PlatformLink
|
|
121
|
+
href="/docs"
|
|
122
|
+
className="text-xs text-muted-foreground hover:text-foreground"
|
|
123
|
+
>
|
|
124
|
+
← All documentation
|
|
125
|
+
</PlatformLink>
|
|
126
|
+
</div>
|
|
127
|
+
</Card>
|
|
128
|
+
</aside>
|
|
129
|
+
|
|
130
|
+
<main className="min-w-0 flex-1">
|
|
131
|
+
<header className="mb-8">
|
|
132
|
+
<p className="text-xs font-semibold tracking-widest text-primary uppercase">
|
|
133
|
+
{eyebrow ?? seriesLabel}
|
|
134
|
+
</p>
|
|
135
|
+
<h1 className="mt-2 text-3xl font-semibold tracking-tight text-balance sm:text-4xl">
|
|
136
|
+
{pageTitle}
|
|
137
|
+
</h1>
|
|
138
|
+
</header>
|
|
139
|
+
|
|
140
|
+
<article
|
|
141
|
+
className={cn(
|
|
142
|
+
'prose max-w-none prose-neutral dark:prose-invert',
|
|
143
|
+
'prose-headings:font-semibold prose-headings:tracking-tight',
|
|
144
|
+
'prose-a:text-primary prose-a:no-underline hover:prose-a:underline',
|
|
145
|
+
'prose-strong:text-foreground',
|
|
146
|
+
'prose-code:rounded prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:text-[0.875em] prose-code:font-medium prose-code:text-foreground prose-code:before:hidden prose-code:after:hidden',
|
|
147
|
+
'prose-table:my-4 prose-table:text-sm',
|
|
148
|
+
'prose-th:border-b prose-th:border-border prose-th:bg-muted/40 prose-th:px-3 prose-th:py-2 prose-th:text-left',
|
|
149
|
+
'prose-td:border-b prose-td:border-border prose-td:px-3 prose-td:py-2',
|
|
150
|
+
)}
|
|
151
|
+
>
|
|
152
|
+
{children}
|
|
153
|
+
</article>
|
|
154
|
+
|
|
155
|
+
<DocsFooter active={active} sections={sections} />
|
|
156
|
+
</main>
|
|
157
|
+
</div>
|
|
158
|
+
</>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function DocsFooter<TId extends string>({
|
|
163
|
+
active,
|
|
164
|
+
sections,
|
|
165
|
+
}: {
|
|
166
|
+
active: TId;
|
|
167
|
+
sections: DocsSection<TId>[];
|
|
168
|
+
}) {
|
|
169
|
+
const idx = sections.findIndex((s) => s.id === active);
|
|
170
|
+
const prev = idx > 0 ? sections[idx - 1] : null;
|
|
171
|
+
const next =
|
|
172
|
+
idx >= 0 && idx < sections.length - 1 ? sections[idx + 1] : null;
|
|
173
|
+
|
|
174
|
+
if (!prev && !next) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<nav className="mt-12 flex items-center justify-between gap-4 border-t border-border pt-6">
|
|
180
|
+
<div>
|
|
181
|
+
{prev && (
|
|
182
|
+
<Button asChild variant="ghost">
|
|
183
|
+
<PlatformLink href={prev.href}>
|
|
184
|
+
<ArrowLeft className="size-4" />
|
|
185
|
+
<span className="flex flex-col items-start">
|
|
186
|
+
<span className="text-[10px] tracking-widest text-muted-foreground uppercase">
|
|
187
|
+
Previous
|
|
188
|
+
</span>
|
|
189
|
+
<span>{prev.title}</span>
|
|
190
|
+
</span>
|
|
191
|
+
</PlatformLink>
|
|
192
|
+
</Button>
|
|
193
|
+
)}
|
|
194
|
+
</div>
|
|
195
|
+
<div>
|
|
196
|
+
{next && (
|
|
197
|
+
<Button asChild variant="ghost">
|
|
198
|
+
<PlatformLink href={next.href}>
|
|
199
|
+
<span className="flex flex-col items-end">
|
|
200
|
+
<span className="text-[10px] tracking-widest text-muted-foreground uppercase">
|
|
201
|
+
Next
|
|
202
|
+
</span>
|
|
203
|
+
<span>{next.title}</span>
|
|
204
|
+
</span>
|
|
205
|
+
<ArrowRight className="size-4" />
|
|
206
|
+
</PlatformLink>
|
|
207
|
+
</Button>
|
|
208
|
+
)}
|
|
209
|
+
</div>
|
|
210
|
+
</nav>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function CodeBlock({
|
|
215
|
+
language,
|
|
216
|
+
children,
|
|
217
|
+
}: {
|
|
218
|
+
language?: string;
|
|
219
|
+
children: string;
|
|
220
|
+
}) {
|
|
221
|
+
return (
|
|
222
|
+
<div className="not-prose my-4 overflow-hidden rounded-lg border border-border bg-muted/40">
|
|
223
|
+
{language && (
|
|
224
|
+
<div className="flex items-center justify-between border-b border-border bg-muted/40 px-3 py-1.5">
|
|
225
|
+
<span className="font-mono text-xs text-muted-foreground">
|
|
226
|
+
{language}
|
|
227
|
+
</span>
|
|
228
|
+
<CopyButton value={children} />
|
|
229
|
+
</div>
|
|
230
|
+
)}
|
|
231
|
+
<pre className="overflow-x-auto p-4 text-sm leading-relaxed text-foreground">
|
|
232
|
+
<code>{children}</code>
|
|
233
|
+
</pre>
|
|
234
|
+
</div>
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function Callout({
|
|
239
|
+
tone = 'info',
|
|
240
|
+
children,
|
|
241
|
+
}: {
|
|
242
|
+
tone?: 'info' | 'warning' | 'success';
|
|
243
|
+
children: React.ReactNode;
|
|
244
|
+
}) {
|
|
245
|
+
const config = {
|
|
246
|
+
info: {
|
|
247
|
+
border: 'border-info-subtle',
|
|
248
|
+
bg: 'bg-info-subtle/40',
|
|
249
|
+
text: 'text-info-fg',
|
|
250
|
+
icon: Info,
|
|
251
|
+
},
|
|
252
|
+
warning: {
|
|
253
|
+
border: 'border-warning-subtle',
|
|
254
|
+
bg: 'bg-warning-subtle/40',
|
|
255
|
+
text: 'text-warning-fg',
|
|
256
|
+
icon: AlertTriangle,
|
|
257
|
+
},
|
|
258
|
+
success: {
|
|
259
|
+
border: 'border-primary/30',
|
|
260
|
+
bg: 'bg-primary/5',
|
|
261
|
+
text: 'text-primary',
|
|
262
|
+
icon: CheckCircle2,
|
|
263
|
+
},
|
|
264
|
+
}[tone];
|
|
265
|
+
|
|
266
|
+
const Icon = config.icon;
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<div
|
|
270
|
+
className={cn(
|
|
271
|
+
'not-prose my-4 flex gap-3 rounded-lg border px-4 py-3 text-sm',
|
|
272
|
+
config.border,
|
|
273
|
+
config.bg,
|
|
274
|
+
)}
|
|
275
|
+
>
|
|
276
|
+
<Icon className={cn('mt-0.5 size-4 shrink-0', config.text)} />
|
|
277
|
+
<div className="text-foreground">{children}</div>
|
|
278
|
+
</div>
|
|
279
|
+
);
|
|
280
|
+
}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import CutoutImage from './cutout-image';
|
|
5
|
+
|
|
6
|
+
type LayerStyle = React.CSSProperties & {
|
|
7
|
+
'--mx'?: string;
|
|
8
|
+
'--my'?: string;
|
|
9
|
+
'--delay'?: string;
|
|
10
|
+
'--float'?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type AssetKey =
|
|
14
|
+
| 'background'
|
|
15
|
+
| 'monitor'
|
|
16
|
+
| 'phone'
|
|
17
|
+
| 'truck'
|
|
18
|
+
| 'pickup'
|
|
19
|
+
| 'van'
|
|
20
|
+
| 'satellite'
|
|
21
|
+
| 'cloud'
|
|
22
|
+
| 'pin';
|
|
23
|
+
|
|
24
|
+
type FleetHeroAnimatedProps = {
|
|
25
|
+
className?: string;
|
|
26
|
+
assetBasePath?: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const defaultAssets: Record<AssetKey, string> = {
|
|
30
|
+
background: 'bg.png',
|
|
31
|
+
monitor: 'pc.png',
|
|
32
|
+
phone: 'mobile.png',
|
|
33
|
+
truck: 'truck.png',
|
|
34
|
+
pickup: 'hilux.png',
|
|
35
|
+
van: 'van.png',
|
|
36
|
+
satellite: 'satellite.png',
|
|
37
|
+
cloud: 'cloud.png',
|
|
38
|
+
pin: 'pin.png',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function joinPath(base: string, file: string) {
|
|
42
|
+
return `${base.replace(/\/$/, '')}/${file}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default function FleetHeroAnimated({
|
|
46
|
+
className = '',
|
|
47
|
+
assetBasePath = '/elements-slider',
|
|
48
|
+
}: FleetHeroAnimatedProps) {
|
|
49
|
+
const wrapRef = useRef<HTMLDivElement | null>(null);
|
|
50
|
+
const [pointer, setPointer] = useState({ x: 0, y: 0 });
|
|
51
|
+
const [loaded, setLoaded] = useState(false);
|
|
52
|
+
|
|
53
|
+
const assets = useMemo(() => {
|
|
54
|
+
return Object.fromEntries(
|
|
55
|
+
Object.entries(defaultAssets).map(([key, value]) => [
|
|
56
|
+
key,
|
|
57
|
+
joinPath(assetBasePath, value),
|
|
58
|
+
]),
|
|
59
|
+
) as Record<AssetKey, string>;
|
|
60
|
+
}, [assetBasePath]);
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const timers = window.setTimeout(() => setLoaded(true), 120);
|
|
64
|
+
|
|
65
|
+
return () => window.clearTimeout(timers);
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
const handlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {
|
|
69
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
70
|
+
const x = ((event.clientX - rect.left) / rect.width - 0.5) * 2;
|
|
71
|
+
const y = ((event.clientY - rect.top) / rect.height - 0.5) * 2;
|
|
72
|
+
setPointer({ x, y });
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const resetPointer = () => setPointer({ x: 0, y: 0 });
|
|
76
|
+
|
|
77
|
+
const parallax = (depth: number): LayerStyle => ({
|
|
78
|
+
'--mx': `${pointer.x * depth}px`,
|
|
79
|
+
'--my': `${pointer.y * depth}px`,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<section
|
|
84
|
+
ref={wrapRef}
|
|
85
|
+
onPointerMove={handlePointerMove}
|
|
86
|
+
onPointerLeave={resetPointer}
|
|
87
|
+
className={`relative isolate mx-auto flex w-full justify-center overflow-hidden bg-white ${className}`}
|
|
88
|
+
aria-label="Fleet, Employee and Assets Tracking and Attendance System"
|
|
89
|
+
>
|
|
90
|
+
<style>{styles}</style>
|
|
91
|
+
|
|
92
|
+
<div
|
|
93
|
+
className={`fleet-hero relative aspect-[561/701] w-full max-w-[1122px] overflow-hidden bg-white transition-opacity duration-700 ${
|
|
94
|
+
loaded ? 'opacity-100' : 'opacity-0'
|
|
95
|
+
}`}
|
|
96
|
+
>
|
|
97
|
+
<img
|
|
98
|
+
src={assets.background}
|
|
99
|
+
alt=""
|
|
100
|
+
className="absolute inset-0 h-full w-full object-cover"
|
|
101
|
+
draggable={false}
|
|
102
|
+
/>
|
|
103
|
+
|
|
104
|
+
<div className="pointer-events-none absolute inset-x-0 top-0 z-20 h-[24%] bg-gradient-to-b from-white via-white/90 to-transparent" />
|
|
105
|
+
<div className="pointer-events-none absolute inset-y-0 left-0 z-20 w-[10%] bg-gradient-to-r from-white to-transparent" />
|
|
106
|
+
<div className="pointer-events-none absolute inset-y-0 right-0 z-20 w-[10%] bg-gradient-to-l from-white to-transparent" />
|
|
107
|
+
|
|
108
|
+
<div className="absolute inset-x-[3%] top-[2.8%] z-30 text-center">
|
|
109
|
+
<div className="mx-auto mb-[1.6%] inline-flex items-center rounded-full border border-emerald-100 bg-white/80 px-4 py-1.5 text-[clamp(10px,1.35vw,15px)] font-semibold text-emerald-800 shadow-sm backdrop-blur">
|
|
110
|
+
Smart Operations Platform
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<h1 className="mx-auto max-w-[920px] text-[clamp(18px,3.1vw,64px)] leading-[0.96] font-black tracking-[-0.045em] text-balance text-neutral-950">
|
|
114
|
+
<span className="block">
|
|
115
|
+
Fleet, Employee & Assets
|
|
116
|
+
</span>
|
|
117
|
+
<span className="block pt-[1.4%]">
|
|
118
|
+
<span className="text-emerald-700">Tracking</span>
|
|
119
|
+
<span className="font-extrabold text-neutral-950">
|
|
120
|
+
{' '}
|
|
121
|
+
and{' '}
|
|
122
|
+
</span>
|
|
123
|
+
<span className="text-emerald-700">
|
|
124
|
+
Attendance System
|
|
125
|
+
</span>
|
|
126
|
+
</span>
|
|
127
|
+
</h1>
|
|
128
|
+
|
|
129
|
+
<p className="mx-auto mt-[2%] max-w-[780px] text-[clamp(12px,1.55vw,18px)] leading-relaxed font-medium text-balance text-neutral-500">
|
|
130
|
+
Real-time monitoring. Workforce visibility. Asset
|
|
131
|
+
oversight. Attendance insights.
|
|
132
|
+
</p>
|
|
133
|
+
|
|
134
|
+
<div className="mx-auto mt-[2.5%] h-[3px] w-[15%] rounded-full bg-gradient-to-r from-emerald-700 via-emerald-500 to-red-500" />
|
|
135
|
+
|
|
136
|
+
<div className="mx-auto mt-[3.2%] grid max-w-[720px] grid-cols-4 gap-[clamp(8px,2vw,28px)] px-[3%]">
|
|
137
|
+
<FeatureIcon icon="⌖" label="Live Tracking" />
|
|
138
|
+
<FeatureIcon icon="●✓" label="Attendance" />
|
|
139
|
+
<FeatureIcon icon="▣" label="Asset Monitoring" />
|
|
140
|
+
<FeatureIcon icon="◉" label="Incident Alerts" />
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<div className="absolute top-[54%] left-[1.4%] z-30 w-[47%]">
|
|
145
|
+
<CutoutImage
|
|
146
|
+
src={assets.monitor}
|
|
147
|
+
alt="Fleet dashboard overview"
|
|
148
|
+
className="fleet-layer fleet-layer-monitor block w-full object-contain drop-shadow-2xl select-none"
|
|
149
|
+
style={parallax(-7)}
|
|
150
|
+
draggable={false}
|
|
151
|
+
/>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<div className="absolute top-[64.5%] left-[35.5%] z-50 w-[16.5%]">
|
|
155
|
+
<CutoutImage
|
|
156
|
+
src={assets.phone}
|
|
157
|
+
alt="Live monitoring mobile app"
|
|
158
|
+
className="fleet-layer fleet-layer-phone block w-full object-contain drop-shadow-2xl select-none"
|
|
159
|
+
style={parallax(11)}
|
|
160
|
+
draggable={false}
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<CutoutImage
|
|
165
|
+
src={assets.van}
|
|
166
|
+
alt="Service van"
|
|
167
|
+
className="fleet-layer fleet-layer-van absolute top-[60%] left-[50.5%] z-20 w-[20%] object-contain opacity-95 drop-shadow-xl select-none"
|
|
168
|
+
style={parallax(-3)}
|
|
169
|
+
draggable={false}
|
|
170
|
+
/>
|
|
171
|
+
|
|
172
|
+
<CutoutImage
|
|
173
|
+
src={assets.truck}
|
|
174
|
+
alt="Sanitation fleet truck"
|
|
175
|
+
className="fleet-layer fleet-layer-truck absolute top-[62%] right-[3.5%] z-40 w-[20%] object-contain drop-shadow-2xl select-none"
|
|
176
|
+
style={parallax(5)}
|
|
177
|
+
draggable={false}
|
|
178
|
+
/>
|
|
179
|
+
|
|
180
|
+
<CutoutImage
|
|
181
|
+
src={assets.pickup}
|
|
182
|
+
alt="Utility pickup vehicle"
|
|
183
|
+
className="fleet-layer fleet-layer-pickup absolute top-[70.2%] right-[25.5%] z-60 w-[24%] object-contain drop-shadow-2xl select-none"
|
|
184
|
+
style={parallax(13)}
|
|
185
|
+
draggable={false}
|
|
186
|
+
/>
|
|
187
|
+
|
|
188
|
+
<CutoutImage
|
|
189
|
+
src={assets.satellite}
|
|
190
|
+
alt=""
|
|
191
|
+
className="fleet-layer fleet-layer-satellite absolute top-[40%] right-[11.5%] z-30 w-[10%] object-contain drop-shadow-lg select-none"
|
|
192
|
+
style={parallax(18)}
|
|
193
|
+
draggable={false}
|
|
194
|
+
/>
|
|
195
|
+
|
|
196
|
+
<CutoutImage
|
|
197
|
+
src={assets.cloud}
|
|
198
|
+
alt=""
|
|
199
|
+
className="fleet-layer fleet-layer-cloud absolute top-[44%] right-[24%] z-30 w-[8%] object-contain drop-shadow-lg select-none"
|
|
200
|
+
style={parallax(10)}
|
|
201
|
+
draggable={false}
|
|
202
|
+
/>
|
|
203
|
+
|
|
204
|
+
<CutoutImage
|
|
205
|
+
src={assets.pin}
|
|
206
|
+
alt=""
|
|
207
|
+
className="fleet-layer fleet-layer-pin-left absolute top-[79.6%] left-[7%] z-40 w-[7%] object-contain drop-shadow-xl select-none"
|
|
208
|
+
style={parallax(16)}
|
|
209
|
+
draggable={false}
|
|
210
|
+
/>
|
|
211
|
+
|
|
212
|
+
<CutoutImage
|
|
213
|
+
src={assets.pin}
|
|
214
|
+
alt=""
|
|
215
|
+
className="fleet-layer fleet-layer-pin-right absolute top-[78.5%] right-[4.5%] z-40 w-[7%] object-contain drop-shadow-xl select-none"
|
|
216
|
+
style={parallax(15)}
|
|
217
|
+
draggable={false}
|
|
218
|
+
/>
|
|
219
|
+
|
|
220
|
+
<div className="absolute inset-x-[6.5%] bottom-[3.8%] z-70 grid grid-cols-4 overflow-hidden rounded-[2rem] bg-gradient-to-r from-emerald-900 via-emerald-800 to-emerald-700 px-[2%] py-[1.8%] text-white shadow-2xl shadow-emerald-900/20 max-sm:rounded-2xl">
|
|
221
|
+
<Benefit icon="🛡" title="Secure & Reliable" />
|
|
222
|
+
<Benefit icon="◷" title="Real-time Monitoring" />
|
|
223
|
+
<Benefit icon="⚙" title="Optimized Operations" />
|
|
224
|
+
<Benefit icon="👥" title="Better Public Services" />
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-80 h-[5%] bg-gradient-to-t from-white via-white/70 to-transparent" />
|
|
228
|
+
</div>
|
|
229
|
+
</section>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function FeatureIcon({ icon, label }: { icon: string; label: string }) {
|
|
234
|
+
return (
|
|
235
|
+
<div className="group flex flex-col items-center gap-2 text-center">
|
|
236
|
+
<div className="grid aspect-square w-[clamp(42px,5.2vw,62px)] place-items-center rounded-full border border-emerald-200 bg-white/80 text-[clamp(17px,2.2vw,28px)] font-black text-emerald-700 shadow-sm backdrop-blur transition-transform duration-300 group-hover:-translate-y-1 group-hover:shadow-md">
|
|
237
|
+
{icon}
|
|
238
|
+
</div>
|
|
239
|
+
<span className="text-[clamp(9px,1.1vw,13px)] leading-tight font-bold text-neutral-800">
|
|
240
|
+
{label}
|
|
241
|
+
</span>
|
|
242
|
+
</div>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function Benefit({ icon, title }: { icon: string; title: string }) {
|
|
247
|
+
return (
|
|
248
|
+
<div className="flex min-w-0 items-center justify-center gap-[8%] border-r border-white/25 px-[3%] last:border-r-0">
|
|
249
|
+
<span className="text-[clamp(18px,3vw,34px)] leading-none opacity-95">
|
|
250
|
+
{icon}
|
|
251
|
+
</span>
|
|
252
|
+
<span className="max-w-[110px] text-[clamp(9px,1.45vw,17px)] leading-tight font-bold">
|
|
253
|
+
{title}
|
|
254
|
+
</span>
|
|
255
|
+
</div>
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const styles = `
|
|
260
|
+
.fleet-layer {
|
|
261
|
+
--mx: 0px;
|
|
262
|
+
--my: 0px;
|
|
263
|
+
--delay: 0ms;
|
|
264
|
+
--float: 8px;
|
|
265
|
+
transform: translate3d(var(--mx), var(--my), 0);
|
|
266
|
+
transition: transform 180ms ease-out;
|
|
267
|
+
will-change: transform;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.fleet-hero {
|
|
271
|
+
transform: translateZ(0);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.fleet-hero::before {
|
|
275
|
+
content: "";
|
|
276
|
+
position: absolute;
|
|
277
|
+
inset: 40% 5% 8% 5%;
|
|
278
|
+
background:
|
|
279
|
+
radial-gradient(circle at 24% 82%, rgba(16, 185, 129, .16), transparent 18%),
|
|
280
|
+
radial-gradient(circle at 74% 65%, rgba(16, 185, 129, .13), transparent 20%);
|
|
281
|
+
pointer-events: none;
|
|
282
|
+
z-index: 10;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.fleet-hero::after {
|
|
286
|
+
content: "";
|
|
287
|
+
position: absolute;
|
|
288
|
+
left: 8%;
|
|
289
|
+
right: 8%;
|
|
290
|
+
bottom: 15.5%;
|
|
291
|
+
height: 22%;
|
|
292
|
+
border-radius: 999px;
|
|
293
|
+
background: linear-gradient(90deg, transparent, rgba(34, 197, 94, .32), transparent);
|
|
294
|
+
filter: blur(22px);
|
|
295
|
+
opacity: .65;
|
|
296
|
+
z-index: 15;
|
|
297
|
+
pointer-events: none;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.fleet-layer-monitor { animation: fleetRise 850ms cubic-bezier(.2,.8,.2,1) both, fleetFloat 7s ease-in-out 900ms infinite; }
|
|
301
|
+
.fleet-layer-phone { animation: fleetRise 950ms cubic-bezier(.2,.8,.2,1) 90ms both, fleetFloatPhone 5.5s ease-in-out 1050ms infinite; }
|
|
302
|
+
.fleet-layer-van { animation: fleetDriveIn 900ms cubic-bezier(.2,.8,.2,1) 170ms both, fleetFloat 8s ease-in-out 1200ms infinite; }
|
|
303
|
+
.fleet-layer-truck { animation: fleetDriveIn 950ms cubic-bezier(.2,.8,.2,1) 240ms both, fleetFloatTruck 6.5s ease-in-out 1300ms infinite; }
|
|
304
|
+
.fleet-layer-pickup { animation: fleetDriveIn 900ms cubic-bezier(.2,.8,.2,1) 320ms both, fleetFloatPickup 5.8s ease-in-out 1400ms infinite; }
|
|
305
|
+
.fleet-layer-satellite { animation: fleetSatelliteIn 1000ms cubic-bezier(.2,.8,.2,1) 420ms both, fleetOrbit 8s ease-in-out 1500ms infinite; transform-origin: 55% 45%; }
|
|
306
|
+
.fleet-layer-cloud { animation: fleetPop 700ms cubic-bezier(.2,.9,.2,1) 520ms both, fleetFloat 6s ease-in-out 1250ms infinite; }
|
|
307
|
+
.fleet-layer-pin-left, .fleet-layer-pin-right { animation: fleetPinDrop 800ms cubic-bezier(.2,.8,.2,1) 550ms both, fleetPulse 2.5s ease-in-out 1450ms infinite; }
|
|
308
|
+
|
|
309
|
+
@keyframes fleetRise {
|
|
310
|
+
from { opacity: 0; transform: translate3d(calc(var(--mx) - 12px), calc(var(--my) + 42px), 0) scale(.96); filter: blur(8px); }
|
|
311
|
+
to { opacity: 1; transform: translate3d(var(--mx), var(--my), 0) scale(1); filter: blur(0); }
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
@keyframes fleetDriveIn {
|
|
315
|
+
from { opacity: 0; transform: translate3d(calc(var(--mx) + 64px), calc(var(--my) + 28px), 0) scale(.94); filter: blur(7px); }
|
|
316
|
+
to { opacity: 1; transform: translate3d(var(--mx), var(--my), 0) scale(1); filter: blur(0); }
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
@keyframes fleetSatelliteIn {
|
|
320
|
+
from { opacity: 0; transform: translate3d(calc(var(--mx) + 38px), calc(var(--my) - 28px), 0) rotate(-14deg) scale(.82); filter: blur(7px); }
|
|
321
|
+
to { opacity: 1; transform: translate3d(var(--mx), var(--my), 0) rotate(0deg) scale(1); filter: blur(0); }
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
@keyframes fleetPop {
|
|
325
|
+
from { opacity: 0; transform: translate3d(var(--mx), var(--my), 0) scale(.65); }
|
|
326
|
+
to { opacity: 1; transform: translate3d(var(--mx), var(--my), 0) scale(1); }
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
@keyframes fleetPinDrop {
|
|
330
|
+
0% { opacity: 0; transform: translate3d(var(--mx), calc(var(--my) - 40px), 0) scale(.85); }
|
|
331
|
+
70% { opacity: 1; transform: translate3d(var(--mx), calc(var(--my) + 5px), 0) scale(1.04); }
|
|
332
|
+
100% { opacity: 1; transform: translate3d(var(--mx), var(--my), 0) scale(1); }
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
@keyframes fleetFloat {
|
|
336
|
+
0%,100% { translate: 0 0; }
|
|
337
|
+
50% { translate: 0 -7px; }
|
|
338
|
+
}
|
|
339
|
+
@keyframes fleetFloatPhone {
|
|
340
|
+
0%,100% { translate: 0 0; rotate: 0deg; }
|
|
341
|
+
50% { translate: 0 -10px; rotate: .5deg; }
|
|
342
|
+
}
|
|
343
|
+
@keyframes fleetFloatTruck {
|
|
344
|
+
0%,100% { translate: 0 0; }
|
|
345
|
+
50% { translate: -4px -6px; }
|
|
346
|
+
}
|
|
347
|
+
@keyframes fleetFloatPickup {
|
|
348
|
+
0%,100% { translate: 0 0; }
|
|
349
|
+
50% { translate: 5px -8px; }
|
|
350
|
+
}
|
|
351
|
+
@keyframes fleetOrbit {
|
|
352
|
+
0%,100% { translate: 0 0; rotate: 0deg; }
|
|
353
|
+
50% { translate: 9px -9px; rotate: 2.5deg; }
|
|
354
|
+
}
|
|
355
|
+
@keyframes fleetPulse {
|
|
356
|
+
0%,100% { scale: 1; filter: drop-shadow(0 10px 14px rgba(220,38,38,.22)); }
|
|
357
|
+
50% { scale: 1.06; filter: drop-shadow(0 16px 22px rgba(220,38,38,.34)); }
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
@media (max-width: 640px) {
|
|
361
|
+
.fleet-layer-monitor { left: -3% !important; top: 49% !important; width: 53% !important; }
|
|
362
|
+
.fleet-layer-phone { left: 39% !important; top: 58% !important; width: 18.5% !important; }
|
|
363
|
+
.fleet-layer-truck { right: -1% !important; top: 61% !important; width: 45% !important; }
|
|
364
|
+
.fleet-layer-pickup { right: 22% !important; top: 72% !important; width: 37% !important; }
|
|
365
|
+
.fleet-layer-van { left: 50% !important; top: 64% !important; width: 26% !important; }
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
@media (prefers-reduced-motion: reduce) {
|
|
369
|
+
.fleet-layer,
|
|
370
|
+
.fleet-layer-monitor,
|
|
371
|
+
.fleet-layer-phone,
|
|
372
|
+
.fleet-layer-van,
|
|
373
|
+
.fleet-layer-truck,
|
|
374
|
+
.fleet-layer-pickup,
|
|
375
|
+
.fleet-layer-satellite,
|
|
376
|
+
.fleet-layer-cloud,
|
|
377
|
+
.fleet-layer-pin-left,
|
|
378
|
+
.fleet-layer-pin-right {
|
|
379
|
+
animation: none !important;
|
|
380
|
+
transition: none !important;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
`;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'react';
|
|
2
|
+
import { cn } from '../lib/utils';
|
|
3
|
+
|
|
4
|
+
export default function InputError({
|
|
5
|
+
message,
|
|
6
|
+
className = '',
|
|
7
|
+
...props
|
|
8
|
+
}: HTMLAttributes<HTMLParagraphElement> & { message?: string }) {
|
|
9
|
+
return message ? (
|
|
10
|
+
<p
|
|
11
|
+
{...props}
|
|
12
|
+
className={cn('text-sm text-red-600 dark:text-red-400', className)}
|
|
13
|
+
>
|
|
14
|
+
{message}
|
|
15
|
+
</p>
|
|
16
|
+
) : null;
|
|
17
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures.
|
|
2
|
+
|
|
3
|
+
To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/docs/#local-fonts
|
|
4
|
+
|
|
5
|
+
You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects.
|
|
6
|
+
|
|
7
|
+
You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection.
|