blixify-ui-web-mcp 0.1.3 → 0.1.19
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/lib/components/action/button/README.md +12 -11
- package/lib/components/action/button/button.stories.tsx +38 -27
- package/lib/components/action/buttonGroup/buttonGroup.stories.tsx +82 -16
- package/lib/components/action/iconButton/README.md +1 -0
- package/lib/components/action/iconButton/iconButton.stories.tsx +6 -12
- package/lib/components/action/radioList/radioList.stories.tsx +10 -16
- package/lib/components/action/slider/slider.stories.tsx +9 -15
- package/lib/components/action/textButton/textButton.stories.tsx +6 -12
- package/lib/components/action/toggle/toggle.stories.tsx +6 -12
- package/lib/components/advanced/collapse/collapse.stories.tsx +240 -24
- package/lib/components/advanced/infoWindow/infoWindow.stories.tsx +10 -16
- package/lib/components/advanced/popUp/README.md +33 -7
- package/lib/components/advanced/popUp/popUp.stories.tsx +9 -15
- package/lib/components/auth/bareSignIn/bareSignIn.stories.tsx +36 -16
- package/lib/components/auth/bareSignUp/bareSignUp.stories.tsx +7 -13
- package/lib/components/auth/signIn/signIn.stories.tsx +4 -10
- package/lib/components/auth/signUp/signUp.stories.tsx +4 -10
- package/lib/components/chart/areaChart/areaChart.stories.tsx +12 -18
- package/lib/components/chart/barChart/barChart.stories.tsx +12 -18
- package/lib/components/chart/boxPlotChart/boxPlotChart.stories.tsx +11 -17
- package/lib/components/chart/bubbleChart/bubbleChart.stories.tsx +11 -17
- package/lib/components/chart/card/card.stories.tsx +172 -38
- package/lib/components/chart/ganttChart/ganttChart.stories.tsx +10 -16
- package/lib/components/chart/heatmapChart/heatmapChart.stories.tsx +11 -17
- package/lib/components/chart/lineChart/lineChart.stories.tsx +8 -14
- package/lib/components/chart/pieChart/pieChart.stories.tsx +11 -17
- package/lib/components/chart/polarAreaChart/polarAreaChart.stories.tsx +11 -17
- package/lib/components/chart/radarChart/radarChart.stories.tsx +6 -12
- package/lib/components/chart/radialBar/radialChart.stories.tsx +4 -10
- package/lib/components/chart/rangeBarChart/rangeBarChart.stories.tsx +8 -14
- package/lib/components/chart/statsCard/statsCard.stories.tsx +12 -18
- package/lib/components/chart/treemapChart/treemapChart.stories.tsx +11 -17
- package/lib/components/chat/chatAnchor/README.md +7 -0
- package/lib/components/chat/chatAnchor/chatAnchor.stories.tsx +15 -21
- package/lib/components/chat/chatBase/README.md +103 -22
- package/lib/components/chat/chatBase/chatBase.stories.tsx +657 -57
- package/lib/components/chat/chatDashboard/README.md +17 -15
- package/lib/components/chat/chatDashboard/chatDashboard.stories.tsx +358 -46
- package/lib/components/data/dataGraph/dataGraph.stories.tsx +8 -14
- package/lib/components/data/dataReference/dataReference.stories.tsx +9 -15
- package/lib/components/data/dataTemplate/README.md +443 -129
- package/lib/components/data/dataTemplate/dataModule.stories.tsx +10 -15
- package/lib/components/data/dataTemplate/dataTemplate.stories.tsx +20 -34
- package/lib/components/data/dataWrapper/README.md +207 -84
- package/lib/components/data/dataWrapper/dataWrapper.consumer.stories.tsx +2125 -0
- package/lib/components/data/dataWrapper/dataWrapper.edit.stories.tsx +607 -0
- package/lib/components/data/dataWrapper/dataWrapper.utils.stories.tsx +576 -0
- package/lib/components/data/dataWrapper/dataWrapper.view.stories.tsx +1273 -0
- package/lib/components/data/flexFilter/flexFilter.stories.tsx +171 -0
- package/lib/components/data/queryForm/queryForm.stories.tsx +6 -12
- package/lib/components/data/readQuery/readQuery.stories.tsx +6 -11
- package/lib/components/data/readQueryHoc/readQueryHoc.stories.tsx +6 -12
- package/lib/components/data/writeQuery/README.md +21 -10
- package/lib/components/data/writeQuery/writeQuery.stories.tsx +6 -10
- package/lib/components/design/actionSection/actionSection.stories.tsx +10 -14
- package/lib/components/design/banner/banner.stories.tsx +6 -12
- package/lib/components/design/bannerAction/bannerAction.stories.tsx +100 -21
- package/lib/components/design/blogSection/blogSection.stories.tsx +6 -12
- package/lib/components/design/carouselSection/carouselSection.stories.tsx +6 -12
- package/lib/components/design/designList/designList.stories.tsx +6 -12
- package/lib/components/design/faq/faq.stories.tsx +53 -31
- package/lib/components/design/fileGrid/fileGrid.stories.tsx +10 -16
- package/lib/components/design/fileViewer/fileViewer.stories.tsx +6 -12
- package/lib/components/design/footerSection/footerSection.stories.tsx +6 -12
- package/lib/components/design/googleReview/googleReview.stories.tsx +6 -12
- package/lib/components/design/heroSection/heroSection.stories.tsx +10 -16
- package/lib/components/design/iconList/iconList.stories.tsx +6 -12
- package/lib/components/design/imageGallery/imageGallery.stories.tsx +6 -12
- package/lib/components/design/imageZoom/imageZoom.stories.tsx +6 -12
- package/lib/components/design/leftContent/leftContent.stories.tsx +6 -12
- package/lib/components/design/logoCloud/logoCloud.stories.tsx +6 -12
- package/lib/components/design/onboarding/onboarding.stories.tsx +8 -14
- package/lib/components/design/pricing/pricing.stories.tsx +6 -12
- package/lib/components/design/rightContent/rightContent.stories.tsx +6 -12
- package/lib/components/design/sectionList/sectionList.stories.tsx +6 -12
- package/lib/components/design/teams/teams.stories.tsx +6 -12
- package/lib/components/display/alert/alert.stories.tsx +8 -14
- package/lib/components/display/calendar/README.md +49 -0
- package/lib/components/display/calendar/calendar.stories.tsx +78 -35
- package/lib/components/display/clipboard/clipboard.stories.tsx +6 -12
- package/lib/components/display/detailList/detailList.stories.tsx +172 -48
- package/lib/components/display/drawer/README.md +44 -11
- package/lib/components/display/drawer/drawer.stories.tsx +71 -16
- package/lib/components/display/emptyState/emptyState.stories.tsx +47 -15
- package/lib/components/display/feeds/feeds.stories.tsx +6 -12
- package/lib/components/display/flexTable/README.md +11 -7
- package/lib/components/display/flexTable/flexTable.stories.tsx +318 -20
- package/lib/components/display/grid/README.md +29 -30
- package/lib/components/display/grid/grid.stories.tsx +16 -23
- package/lib/components/display/kanban/kanban.stories.tsx +17 -23
- package/lib/components/display/list/README.md +30 -29
- package/lib/components/display/list/list.stories.tsx +11 -17
- package/lib/components/display/loading/loading.stories.tsx +7 -12
- package/lib/components/display/mapView/mapView.stories.tsx +9 -15
- package/lib/components/display/modal/modal.stories.tsx +6 -13
- package/lib/components/display/newCalendar/README.md +163 -0
- package/lib/components/display/newCalendar/newCalendar.stories.tsx +292 -0
- package/lib/components/display/newFeeds/README.md +53 -0
- package/lib/components/display/newFeeds/newFeeds.stories.tsx +104 -0
- package/lib/components/display/newKanban/README.md +25 -0
- package/lib/components/display/newKanban/newKanban.stories.tsx +131 -0
- package/lib/components/display/newList/README.md +30 -0
- package/lib/components/display/newList/newList.stories.tsx +231 -0
- package/lib/components/display/newPagination/README.md +15 -0
- package/lib/components/display/newTable/README.md +121 -0
- package/lib/components/display/newTable/newTable.stories.tsx +551 -0
- package/lib/components/display/notification/notification.stories.tsx +6 -12
- package/lib/components/display/progress/progress.stories.tsx +7 -12
- package/lib/components/display/stepper/README.md +4 -0
- package/lib/components/display/stepper/stepper.stories.tsx +70 -23
- package/lib/components/display/table/README.md +38 -37
- package/lib/components/display/table/table.stories.tsx +34 -36
- package/lib/components/display/tag/tag.stories.tsx +34 -40
- package/lib/components/input/address/address.stories.tsx +12 -18
- package/lib/components/input/canvas/canvas.stories.tsx +12 -18
- package/lib/components/input/checkbox/checkbox.stories.tsx +7 -13
- package/lib/components/input/datePicker/datePicker.stories.tsx +41 -11
- package/lib/components/input/form/form.stories.tsx +11 -17
- package/lib/components/input/markdown/README.md +80 -2
- package/lib/components/input/markdown/markdown.stories.tsx +83 -32
- package/lib/components/input/monthYearPicker/monthYearPicker.stories.tsx +52 -24
- package/lib/components/input/netPromoterScore/netPromoterScore.stories.tsx +6 -12
- package/lib/components/input/rating/rating.stories.tsx +6 -12
- package/lib/components/input/select/README.md +31 -29
- package/lib/components/input/select/select.stories.tsx +33 -39
- package/lib/components/input/signature/signature.stories.tsx +12 -18
- package/lib/components/input/textArea/textArea.stories.tsx +6 -12
- package/lib/components/input/textInput/textinput.stories.tsx +14 -20
- package/lib/components/input/uploadInput/uploadInput.stories.tsx +143 -19
- package/lib/components/navigation/bars/README.md +27 -0
- package/lib/components/navigation/bars/bars.stories.tsx +56 -19
- package/lib/components/navigation/bottomTab/bottomTab.stories.tsx +7 -12
- package/lib/components/navigation/breadcrumb/breadcrumb.stories.tsx +8 -14
- package/lib/components/navigation/header/header.stories.tsx +33 -21
- package/lib/components/navigation/leftNav/leftNav.stories.tsx +10 -16
- package/lib/components/navigation/tabs/tabs.stories.tsx +10 -16
- package/lib/components/structure/column/column.stories.tsx +10 -15
- package/lib/components/structure/container/container.stories.tsx +8 -13
- package/lib/components/structure/link/link.stories.tsx +11 -17
- package/lib/components/structure/text/text.stories.tsx +12 -17
- package/lib/components/template/authPage/authPage.stories.tsx +6 -5
- package/lib/components/template/calendarView/calendarView.stories.tsx +726 -0
- package/lib/components/template/chatDashboard/chatDashboard.stories.tsx +497 -0
- package/lib/components/template/landingPage/landingPage.stories.tsx +10 -7
- package/lib/components/template/mapLayout/mapLayout.stories.tsx +519 -0
- package/lib/components/template/productListing/productListing.stories.tsx +7 -13
- package/lib/components/template/productOverview/productOverview.stories.tsx +5 -11
- package/lib/components/template/shoppingCart/shoppingCart.stories.tsx +6 -12
- package/lib/components/template/userHomePage/userHomePage.stories.tsx +384 -524
- package/lib/components/template/userRankingDashboard/userRankingDashboard.stories.tsx +145 -75
- package/lib/mcp/tools/componentsRoot.d.ts +2 -0
- package/lib/mcp/tools/componentsRoot.d.ts.map +1 -0
- package/lib/mcp/tools/componentsRoot.js +59 -0
- package/lib/mcp/tools/componentsRoot.js.map +1 -0
- package/lib/mcp/tools/getBackendComponent.d.ts.map +1 -1
- package/lib/mcp/tools/getBackendComponent.js +2 -5
- package/lib/mcp/tools/getBackendComponent.js.map +1 -1
- package/lib/mcp/tools/getComponent.d.ts.map +1 -1
- package/lib/mcp/tools/getComponent.js +2 -5
- package/lib/mcp/tools/getComponent.js.map +1 -1
- package/lib/mcp/tools/listComponents.d.ts.map +1 -1
- package/lib/mcp/tools/listComponents.js +15 -10
- package/lib/mcp/tools/listComponents.js.map +1 -1
- package/lib/mcp/tools/suggestTemplate.d.ts.map +1 -1
- package/lib/mcp/tools/suggestTemplate.js +107 -21
- package/lib/mcp/tools/suggestTemplate.js.map +1 -1
- package/package.json +1 -1
- package/lib/components/data/dataWrapper/dataWrapper.stories.tsx +0 -831
|
@@ -1,605 +1,465 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Disclosure, Menu, Popover, Transition } from "@headlessui/react";
|
|
2
|
+
import {
|
|
3
|
+
ArrowDownTrayIcon,
|
|
4
|
+
ArrowPathIcon,
|
|
5
|
+
ArrowTrendingUpIcon,
|
|
6
|
+
BellIcon,
|
|
7
|
+
CheckCircleIcon,
|
|
8
|
+
FireIcon,
|
|
9
|
+
PlusIcon,
|
|
10
|
+
SparklesIcon,
|
|
11
|
+
} from "@heroicons/react/24/outline";
|
|
12
|
+
import {
|
|
13
|
+
ChartBarIcon,
|
|
14
|
+
HomeIcon,
|
|
15
|
+
ListBulletIcon,
|
|
16
|
+
UserIcon,
|
|
17
|
+
} from "@heroicons/react/24/solid";
|
|
2
18
|
import React, { useState } from "react";
|
|
3
19
|
import { Button } from "../../action/button";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { IconList } from "../../design/iconList";
|
|
9
|
-
import { Grid, GridClass } from "../../display/grid";
|
|
10
|
-
import { Modal } from "../../display/modal";
|
|
11
|
-
import { Select } from "../../input/select";
|
|
12
|
-
import { TextInput } from "../../input/textInput";
|
|
20
|
+
import { ButtonGroup } from "../../action/buttonGroup";
|
|
21
|
+
import { AreaChart } from "../../chart/areaChart";
|
|
22
|
+
import { Card } from "../../chart/card";
|
|
23
|
+
import { Collapse } from "../../advanced/collapse";
|
|
13
24
|
import { Container } from "../../structure/container";
|
|
25
|
+
import { FooterSection } from "../../design/footerSection";
|
|
26
|
+
import { BottomTab } from "../../navigation/bottomTab";
|
|
14
27
|
import { Header } from "../../navigation/header";
|
|
15
|
-
import "
|
|
16
|
-
import {
|
|
17
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
18
|
-
const Logo = require("../../../assets/logo.png");
|
|
28
|
+
import { NewList, NewListItem } from "../../display/newList";
|
|
29
|
+
import { Progress } from "../../display/progress";
|
|
19
30
|
|
|
20
|
-
const
|
|
31
|
+
const meta = { title: "Template/App Home Page" };
|
|
32
|
+
export default meta;
|
|
21
33
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
</svg>
|
|
40
|
-
),
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
title: "Monthly Electricity Bill Over RM 200",
|
|
44
|
-
desc: "The higher your bill, the more you stand to save with solar.",
|
|
45
|
-
icon: (
|
|
46
|
-
<svg
|
|
47
|
-
className="w-6 h-6"
|
|
48
|
-
fill="none"
|
|
49
|
-
viewBox="0 0 24 24"
|
|
50
|
-
stroke="currentColor"
|
|
51
|
-
>
|
|
52
|
-
<path
|
|
53
|
-
strokeLinecap="round"
|
|
54
|
-
strokeLinejoin="round"
|
|
55
|
-
strokeWidth={2}
|
|
56
|
-
d="M13 10V3L4 14h7v7l9-11h-7z"
|
|
57
|
-
/>
|
|
58
|
-
</svg>
|
|
59
|
-
),
|
|
60
|
-
},
|
|
34
|
+
|
|
35
|
+
const Logo = new URL("../../../assets/logo.png", import.meta.url).href;
|
|
36
|
+
|
|
37
|
+
// ─── Sample data (domain-neutral) ───────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
const TODAY = new Date();
|
|
40
|
+
const fmtDate = TODAY.toLocaleDateString(undefined, {
|
|
41
|
+
weekday: "long",
|
|
42
|
+
day: "numeric",
|
|
43
|
+
month: "long",
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const NAVIGATION = [
|
|
47
|
+
{ name: "Home", href: "#" },
|
|
48
|
+
{ name: "Activity", href: "#" },
|
|
49
|
+
{ name: "Insights", href: "#" },
|
|
50
|
+
{ name: "Settings", href: "#" },
|
|
61
51
|
];
|
|
62
52
|
|
|
63
|
-
const
|
|
64
|
-
{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
53
|
+
const USER_NAV = [
|
|
54
|
+
{ name: "Your profile", action: () => {} },
|
|
55
|
+
{ name: "Sign out", action: () => {} },
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
const USER = {
|
|
59
|
+
name: "Louis Ong",
|
|
60
|
+
email: "louis@blixify.co",
|
|
61
|
+
image: Logo,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// INFO: BottomTab uses solid heroicons + href links. Mirrors the desktop
|
|
65
|
+
// Header nav so mobile users get the same surface in thumb-reach.
|
|
66
|
+
const BOTTOM_TABS = [
|
|
67
|
+
{ icon: HomeIcon, title: "Home", href: "#" },
|
|
68
|
+
{ icon: ChartBarIcon, title: "Insights", href: "#" },
|
|
69
|
+
{ icon: ListBulletIcon, title: "Activity", href: "#" },
|
|
70
|
+
{ icon: UserIcon, title: "Profile", href: "#" },
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
// INFO: FooterSection expects FooterList[] with title + subList. Mirrors
|
|
74
|
+
// the header nav so signed-in users get the same surface in both places.
|
|
75
|
+
const FOOTER_LIST = [
|
|
75
76
|
{
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
imageSrc:
|
|
79
|
-
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?auto=format&fit=facearea&facepad=2&w=400&h=300&q=80",
|
|
80
|
-
name: "Ahmad Razif",
|
|
81
|
-
location: "Johor Bahru, Johor",
|
|
82
|
-
totalSaving: "RM 320/mo",
|
|
83
|
-
monthlySaving: "RM 160/mo",
|
|
84
|
-
videoUrl: "",
|
|
77
|
+
title: "Navigate",
|
|
78
|
+
subList: NAVIGATION.map((n) => ({ name: n.name, link: n.href })),
|
|
85
79
|
},
|
|
86
80
|
{
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
"
|
|
91
|
-
|
|
92
|
-
location: "George Town, Penang",
|
|
93
|
-
totalSaving: "RM 380/mo",
|
|
94
|
-
monthlySaving: "RM 152/mo",
|
|
95
|
-
videoUrl: "",
|
|
81
|
+
title: "Account",
|
|
82
|
+
subList: [
|
|
83
|
+
{ name: "Profile", link: "#" },
|
|
84
|
+
{ name: "Sign out", link: "#" },
|
|
85
|
+
],
|
|
96
86
|
},
|
|
97
87
|
];
|
|
98
88
|
|
|
99
|
-
const
|
|
100
|
-
{
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
89
|
+
const QUICK_ACTIONS = [
|
|
90
|
+
{ id: "add", label: "Add entry", icon: PlusIcon },
|
|
91
|
+
{ id: "new", label: "New record", icon: SparklesIcon },
|
|
92
|
+
{ id: "sync", label: "Sync", icon: ArrowPathIcon },
|
|
93
|
+
{ id: "export", label: "Export", icon: ArrowDownTrayIcon },
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
const KPI_TILES = [
|
|
106
97
|
{
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
98
|
+
title: "Streak",
|
|
99
|
+
count: 12,
|
|
100
|
+
unit: "days",
|
|
101
|
+
description: "Best: 24 days",
|
|
102
|
+
trend: "On track",
|
|
103
|
+
icon: FireIcon,
|
|
111
104
|
},
|
|
112
105
|
{
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
106
|
+
title: "This Week",
|
|
107
|
+
count: 547,
|
|
108
|
+
unit: "pts",
|
|
109
|
+
description: "Mon — Sun",
|
|
110
|
+
trend: "+18% MoM",
|
|
111
|
+
icon: SparklesIcon,
|
|
117
112
|
},
|
|
118
113
|
];
|
|
119
114
|
|
|
120
|
-
|
|
115
|
+
// INFO: 7-day trend for the area chart — 1 series, last 7 days
|
|
116
|
+
const TREND_CATEGORIES = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
117
|
+
const TREND_SERIES = [
|
|
118
|
+
{ name: "Score", data: [62, 71, 65, 78, 74, 82, 82] },
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
// INFO: progress bars are visualisations (chart-adjacent), so they keep
|
|
122
|
+
// distinct fill colours — but the palette is intentionally cohesive
|
|
123
|
+
// (primary -> teal -> indigo) rather than the previous primary/emerald/amber
|
|
124
|
+
// rainbow.
|
|
125
|
+
const PROGRESS_BARS = [
|
|
121
126
|
{
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
fill="none"
|
|
129
|
-
viewBox="0 0 24 24"
|
|
130
|
-
stroke="currentColor"
|
|
131
|
-
>
|
|
132
|
-
<path
|
|
133
|
-
strokeLinecap="round"
|
|
134
|
-
strokeLinejoin="round"
|
|
135
|
-
strokeWidth={2}
|
|
136
|
-
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
137
|
-
/>
|
|
138
|
-
</svg>
|
|
139
|
-
),
|
|
127
|
+
id: "p1",
|
|
128
|
+
label: "Goal A",
|
|
129
|
+
current: 6,
|
|
130
|
+
target: 8,
|
|
131
|
+
unit: "hrs",
|
|
132
|
+
fill: "bg-gradient-to-r from-primary-500 to-primary-400",
|
|
140
133
|
},
|
|
141
134
|
{
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
viewBox="0 0 24 24"
|
|
149
|
-
stroke="currentColor"
|
|
150
|
-
>
|
|
151
|
-
<path
|
|
152
|
-
strokeLinecap="round"
|
|
153
|
-
strokeLinejoin="round"
|
|
154
|
-
strokeWidth={2}
|
|
155
|
-
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
|
|
156
|
-
/>
|
|
157
|
-
</svg>
|
|
158
|
-
),
|
|
135
|
+
id: "p2",
|
|
136
|
+
label: "Goal B",
|
|
137
|
+
current: 1850,
|
|
138
|
+
target: 2200,
|
|
139
|
+
unit: "kcal",
|
|
140
|
+
fill: "bg-gradient-to-r from-teal-500 to-teal-400",
|
|
159
141
|
},
|
|
160
142
|
{
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
viewBox="0 0 24 24"
|
|
168
|
-
stroke="currentColor"
|
|
169
|
-
>
|
|
170
|
-
<path
|
|
171
|
-
strokeLinecap="round"
|
|
172
|
-
strokeLinejoin="round"
|
|
173
|
-
strokeWidth={2}
|
|
174
|
-
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
|
175
|
-
/>
|
|
176
|
-
</svg>
|
|
177
|
-
),
|
|
143
|
+
id: "p3",
|
|
144
|
+
label: "Goal C",
|
|
145
|
+
current: 7200,
|
|
146
|
+
target: 10000,
|
|
147
|
+
unit: "steps",
|
|
148
|
+
fill: "bg-gradient-to-r from-indigo-500 to-indigo-400",
|
|
178
149
|
},
|
|
179
150
|
];
|
|
180
151
|
|
|
181
|
-
|
|
152
|
+
// INFO: recent activity uses emoji as the avatar — replaces the previous
|
|
153
|
+
// icon-with-coloured-background pattern. Emoji adds personality without the
|
|
154
|
+
// visual noise of multiple coloured circles competing for attention.
|
|
155
|
+
const RECENT_ITEMS: NewListItem[] = [
|
|
182
156
|
{
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
157
|
+
id: "r1",
|
|
158
|
+
title: "Activity logged",
|
|
159
|
+
description: "Recorded 45 mins · 320 kcal",
|
|
160
|
+
icon: <span className="text-2xl">🏃</span>,
|
|
161
|
+
date: new Date(TODAY.getTime() - 1000 * 60 * 45),
|
|
186
162
|
},
|
|
187
163
|
{
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
164
|
+
id: "r2",
|
|
165
|
+
title: "Reminder dismissed",
|
|
166
|
+
description: "Daily check-in completed",
|
|
167
|
+
icon: <span className="text-2xl">🔔</span>,
|
|
168
|
+
date: new Date(TODAY.getTime() - 1000 * 60 * 60 * 2),
|
|
191
169
|
},
|
|
192
170
|
{
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
171
|
+
id: "r3",
|
|
172
|
+
title: "Entry created",
|
|
173
|
+
description: "Manual entry · 240 kcal",
|
|
174
|
+
icon: <span className="text-2xl">📝</span>,
|
|
175
|
+
date: new Date(TODAY.getTime() - 1000 * 60 * 60 * 4),
|
|
196
176
|
},
|
|
197
177
|
{
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
178
|
+
id: "r4",
|
|
179
|
+
title: "Sync completed",
|
|
180
|
+
description: "12 new items synced",
|
|
181
|
+
icon: <span className="text-2xl">🔄</span>,
|
|
182
|
+
date: new Date(TODAY.getTime() - 1000 * 60 * 60 * 6),
|
|
201
183
|
},
|
|
202
184
|
];
|
|
203
185
|
|
|
204
|
-
const
|
|
186
|
+
const REMINDER_ITEMS: NewListItem[] = [
|
|
205
187
|
{
|
|
206
|
-
id: "
|
|
207
|
-
title: "
|
|
208
|
-
description: "
|
|
188
|
+
id: "rm1",
|
|
189
|
+
title: "Weekly review",
|
|
190
|
+
description: "Schedule pending — Sun 9:00 AM",
|
|
191
|
+
icon: <span className="text-2xl">⏰</span>,
|
|
209
192
|
},
|
|
210
193
|
{
|
|
211
|
-
id: "
|
|
212
|
-
title: "
|
|
213
|
-
description: "
|
|
194
|
+
id: "rm2",
|
|
195
|
+
title: "Profile incomplete",
|
|
196
|
+
description: "Add your preferences to personalise recommendations",
|
|
197
|
+
icon: <span className="text-2xl">👤</span>,
|
|
214
198
|
},
|
|
215
199
|
];
|
|
216
200
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
201
|
+
// ─── Inline sub-components ──────────────────────────────────────────────────
|
|
202
|
+
|
|
203
|
+
// INFO: HeroCard keeps the primary->violet gradient — one decorative moment
|
|
204
|
+
// at the top sets the tone, but everything below it standardises on
|
|
205
|
+
// `primary` so the eye isn't pulled in 5 directions. Headers / heroes are
|
|
206
|
+
// not interactive UI; the rule "interactive = primary only" still holds.
|
|
207
|
+
const HeroCard = ({ name, streak }: { name: string; streak: number }) => (
|
|
208
|
+
<div className="relative mt-4 overflow-hidden rounded-2xl bg-gradient-to-br from-primary-500 via-primary-400 to-violet-500 p-5 text-white shadow-lg md:p-6">
|
|
209
|
+
<div className="pointer-events-none absolute -right-10 -top-10 h-40 w-40 rounded-full bg-white/10 blur-2xl" />
|
|
210
|
+
<div className="pointer-events-none absolute -bottom-12 -left-8 h-32 w-32 rounded-full bg-white/10 blur-2xl" />
|
|
211
|
+
<div className="relative">
|
|
212
|
+
<p className="text-xs font-medium uppercase tracking-wide text-white/80">
|
|
213
|
+
{fmtDate}
|
|
214
|
+
</p>
|
|
215
|
+
<h1 className="mt-1 text-2xl font-bold md:text-3xl">
|
|
216
|
+
Good morning, {name} 👋
|
|
217
|
+
</h1>
|
|
218
|
+
<div className="mt-3 inline-flex items-center gap-1.5 rounded-full bg-white/20 px-3 py-1 text-xs font-semibold backdrop-blur">
|
|
219
|
+
<FireIcon className="h-3.5 w-3.5 text-white" />
|
|
220
|
+
<span>{streak} day streak — keep it going!</span>
|
|
235
221
|
</div>
|
|
236
222
|
</div>
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const ProgressBar = ({
|
|
227
|
+
label,
|
|
228
|
+
current,
|
|
229
|
+
target,
|
|
230
|
+
unit,
|
|
231
|
+
fill,
|
|
232
|
+
}: {
|
|
233
|
+
label: string;
|
|
234
|
+
current: number;
|
|
235
|
+
target: number;
|
|
236
|
+
unit: string;
|
|
237
|
+
fill: string;
|
|
238
|
+
}) => {
|
|
239
|
+
const pct = Math.min(100, Math.round((current / target) * 100));
|
|
240
|
+
const isComplete = pct >= 100;
|
|
241
|
+
return (
|
|
242
|
+
<div>
|
|
243
|
+
<div className="mb-1.5 flex items-baseline justify-between">
|
|
244
|
+
<div className="flex items-center gap-2">
|
|
245
|
+
<p className="text-sm font-medium text-gray-800">{label}</p>
|
|
246
|
+
{isComplete && (
|
|
247
|
+
<span className="inline-flex items-center gap-1 rounded-full bg-primary-100 px-2 py-0.5 text-[10px] font-bold uppercase text-primary-700">
|
|
248
|
+
<CheckCircleIcon className="h-3 w-3" />
|
|
249
|
+
Done
|
|
250
|
+
</span>
|
|
251
|
+
)}
|
|
252
|
+
</div>
|
|
253
|
+
<p className="text-xs text-gray-500">
|
|
254
|
+
<span className="font-semibold text-gray-900">{current}</span>
|
|
255
|
+
{" / "}
|
|
256
|
+
{target} {unit}
|
|
255
257
|
</p>
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
258
|
+
</div>
|
|
259
|
+
<div className="h-2.5 w-full overflow-hidden rounded-full bg-gray-100">
|
|
260
|
+
<div
|
|
261
|
+
className={`h-full rounded-full transition-all ${fill}`}
|
|
262
|
+
style={{ width: `${pct}%` }}
|
|
263
|
+
/>
|
|
259
264
|
</div>
|
|
260
265
|
</div>
|
|
261
|
-
)
|
|
266
|
+
);
|
|
262
267
|
};
|
|
263
268
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const customGridItems: { [id: string]: React.ReactElement } = {};
|
|
277
|
-
USE_CASE_ITEMS.forEach((item) => {
|
|
278
|
-
customGridItems[item.id] = (
|
|
279
|
-
<div className="w-72 h-full flex flex-col rounded-3xl shadow-lg overflow-hidden lg:w-full hover:shadow-xl">
|
|
280
|
-
<img
|
|
281
|
-
src={item.imageSrc}
|
|
282
|
-
alt={item.name}
|
|
283
|
-
className="h-48 object-cover rounded-t-3xl"
|
|
284
|
-
/>
|
|
285
|
-
<div className="-mt-6 p-6 bg-primary-900 text-white">
|
|
286
|
-
<h1 className="text-5xl font-bold">{item.percentage}</h1>
|
|
287
|
-
<p>reduction in bill</p>
|
|
288
|
-
<p className="text-primary-300 text-xl font-semibold">
|
|
289
|
-
From {item.totalSaving} to {item.monthlySaving}
|
|
290
|
-
</p>
|
|
291
|
-
<div className="mt-8 flex justify-between items-center">
|
|
292
|
-
<div className="text-sm text-gray-300">
|
|
293
|
-
<p>{item.name}</p>
|
|
294
|
-
<p>{item.location}</p>
|
|
295
|
-
</div>
|
|
296
|
-
{item.videoUrl && (
|
|
297
|
-
<button
|
|
298
|
-
onClick={() => {
|
|
299
|
-
setVideoUrl(item.videoUrl!);
|
|
300
|
-
setVideoModal(true);
|
|
301
|
-
}}
|
|
302
|
-
className="text-primary-300 hover:text-white"
|
|
303
|
-
>
|
|
304
|
-
<svg
|
|
305
|
-
className="w-8 h-8"
|
|
306
|
-
fill="currentColor"
|
|
307
|
-
viewBox="0 0 24 24"
|
|
308
|
-
>
|
|
309
|
-
<path d="M8 5v14l11-7z" />
|
|
310
|
-
</svg>
|
|
311
|
-
</button>
|
|
312
|
-
)}
|
|
313
|
-
</div>
|
|
314
|
-
</div>
|
|
315
|
-
</div>
|
|
316
|
-
);
|
|
317
|
-
});
|
|
269
|
+
const TrendChip = ({ text }: { text: string }) => (
|
|
270
|
+
<div className="inline-flex items-center gap-1 text-xs font-medium text-primary-600">
|
|
271
|
+
<ArrowTrendingUpIcon className="h-3.5 w-3.5" />
|
|
272
|
+
<span>{text}</span>
|
|
273
|
+
</div>
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
// ─── Story ─────────────────────────────────────────────────────────────────
|
|
277
|
+
|
|
278
|
+
export const AppHomePage = () => {
|
|
279
|
+
const [activeAction, setActiveAction] = useState<string | null>(null);
|
|
318
280
|
|
|
319
281
|
return (
|
|
320
|
-
<div className="bg-
|
|
282
|
+
<div className="min-h-screen bg-gray-50">
|
|
283
|
+
{/* HEADER — sticky top nav with logo, links, avatar */}
|
|
321
284
|
<Header
|
|
322
285
|
defaultNav="/"
|
|
323
|
-
page="
|
|
286
|
+
page="Home"
|
|
324
287
|
logo={Logo}
|
|
325
|
-
navigation={
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
{ name: "Calculator", href: "#" },
|
|
329
|
-
{ name: "About Us", href: "#" },
|
|
330
|
-
]}
|
|
288
|
+
navigation={NAVIGATION}
|
|
289
|
+
userNavigation={USER_NAV}
|
|
290
|
+
user={USER}
|
|
331
291
|
lib={{ Disclosure, Menu, Transition, Popover }}
|
|
332
292
|
/>
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
293
|
+
|
|
294
|
+
<Container bgColor="bg-gray-50" className="pb-28">
|
|
295
|
+
<HeroCard name="Louis" streak={12} />
|
|
296
|
+
|
|
297
|
+
{/* Quick actions — selectable pill group via ButtonGroup */}
|
|
298
|
+
<ButtonGroup
|
|
299
|
+
containerClassName="mt-4"
|
|
300
|
+
variant="pill"
|
|
301
|
+
selectedId={activeAction}
|
|
302
|
+
onClick={(id) => setActiveAction(id)}
|
|
303
|
+
selection={QUICK_ACTIONS.map((a) => {
|
|
304
|
+
const Icon = a.icon;
|
|
305
|
+
const isActive = activeAction === a.id;
|
|
306
|
+
return {
|
|
307
|
+
id: a.id,
|
|
308
|
+
name: a.label,
|
|
309
|
+
icon: (
|
|
310
|
+
<Icon
|
|
311
|
+
className={`h-4 w-4 ${
|
|
312
|
+
isActive ? "text-white" : "text-primary-600"
|
|
313
|
+
}`}
|
|
314
|
+
/>
|
|
315
|
+
),
|
|
316
|
+
};
|
|
317
|
+
})}
|
|
318
|
+
/>
|
|
319
|
+
|
|
320
|
+
{/* Score + KPI tiles — mobile: Score spans both columns then
|
|
321
|
+
Streak + This Week share a row. Desktop (md+): all three sit
|
|
322
|
+
in a single 3-col row so the score tile no longer floats in
|
|
323
|
+
~1200px of empty space. */}
|
|
324
|
+
<div className="mt-4 grid grid-cols-2 gap-3 md:grid-cols-3">
|
|
325
|
+
<div className="col-span-2 rounded-2xl border border-gray-200 bg-white p-4 shadow-sm md:col-span-1">
|
|
326
|
+
<div className="flex items-baseline justify-between">
|
|
327
|
+
<p className="text-sm font-medium text-gray-500">Today's Score</p>
|
|
328
|
+
<TrendChip text="+5 vs yesterday" />
|
|
329
|
+
</div>
|
|
330
|
+
<div className="mt-1 flex items-baseline gap-1">
|
|
331
|
+
<p className="text-4xl font-bold text-gray-900">82</p>
|
|
332
|
+
<p className="text-lg font-medium text-gray-400">/100</p>
|
|
333
|
+
</div>
|
|
334
|
+
<div className="mt-3">
|
|
335
|
+
<Progress percentage={82} />
|
|
336
|
+
</div>
|
|
358
337
|
</div>
|
|
359
|
-
|
|
338
|
+
{KPI_TILES.map((kpi) => {
|
|
339
|
+
const Icon = kpi.icon;
|
|
340
|
+
return (
|
|
341
|
+
<Card
|
|
342
|
+
key={kpi.title}
|
|
343
|
+
size="compact"
|
|
344
|
+
icon={<Icon className="h-4 w-4 text-primary-600" />}
|
|
345
|
+
iconBgColor="bg-primary-100"
|
|
346
|
+
title={kpi.title}
|
|
347
|
+
count={kpi.count}
|
|
348
|
+
unit={kpi.unit}
|
|
349
|
+
description={kpi.description}
|
|
350
|
+
subContent={<TrendChip text={kpi.trend} />}
|
|
351
|
+
/>
|
|
352
|
+
);
|
|
353
|
+
})}
|
|
360
354
|
</div>
|
|
361
|
-
</div>
|
|
362
355
|
|
|
363
|
-
|
|
364
|
-
<
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
allowFullScreen
|
|
356
|
+
{/* TREND CHART — single focal data viz, compact at ~200px */}
|
|
357
|
+
<div className="mt-3">
|
|
358
|
+
<div className="overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-sm">
|
|
359
|
+
<AreaChart
|
|
360
|
+
title="Last 7 days"
|
|
361
|
+
categories={TREND_CATEGORIES}
|
|
362
|
+
stackedAreaSeries={TREND_SERIES}
|
|
363
|
+
chartSize={200}
|
|
364
|
+
showToolbar={false}
|
|
373
365
|
/>
|
|
374
|
-
)}
|
|
375
|
-
onClose={() => setVideoModal(false)}
|
|
376
|
-
maxWidth="4xl"
|
|
377
|
-
/>
|
|
378
|
-
<Container className="pb-20 pt-10">
|
|
379
|
-
<div className="flex flex-col justify-center items-center text-center">
|
|
380
|
-
<h1 className="text-xl md:text-3xl">If you meet both criteria,</h1>
|
|
381
|
-
<h1 className="text-3xl font-semibold text-primary-800 md:text-5xl md:mt-2">
|
|
382
|
-
You are a qualified applicant!
|
|
383
|
-
</h1>
|
|
384
366
|
</div>
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
<div className="mt-14">
|
|
406
|
-
<Grid
|
|
407
|
-
data={gridItems}
|
|
408
|
-
hidePagination
|
|
409
|
-
loading={false}
|
|
410
|
-
customGridItems={customGridItems}
|
|
411
|
-
itemClassName="mb-5 !bg-gray-50 shadow-none w-full"
|
|
412
|
-
gridColClassName="mb-0 gap-6 grid-cols-1 sm:grid-cols-3 md:gap-8"
|
|
413
|
-
horizontalScroll
|
|
367
|
+
</div>
|
|
368
|
+
|
|
369
|
+
{/* Today's progress + Reminders — stacked on mobile, side-by-side
|
|
370
|
+
on desktop (md+) so each section gets ~50% of the row instead
|
|
371
|
+
of stretching its single goal bar across the full container. */}
|
|
372
|
+
<div className="mt-4 grid gap-4 md:grid-cols-2">
|
|
373
|
+
<div className="overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-sm">
|
|
374
|
+
<Collapse
|
|
375
|
+
title="Today's progress"
|
|
376
|
+
summary={`${PROGRESS_BARS.length} goals`}
|
|
377
|
+
defaultOpen
|
|
378
|
+
icon={<CheckCircleIcon className="h-5 w-5 text-primary-500" />}
|
|
379
|
+
contentClassName="space-y-4 px-4 py-4"
|
|
380
|
+
content={
|
|
381
|
+
<>
|
|
382
|
+
{PROGRESS_BARS.map((p) => (
|
|
383
|
+
<ProgressBar key={p.id} {...p} />
|
|
384
|
+
))}
|
|
385
|
+
</>
|
|
386
|
+
}
|
|
414
387
|
/>
|
|
415
388
|
</div>
|
|
416
|
-
</Container>
|
|
417
|
-
</div>
|
|
418
389
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
390
|
+
{/* Reminders — Modern Mobile pattern from Collapse stories: bold
|
|
391
|
+
3-stop primary gradient on collapseContainerCss + notCollapse
|
|
392
|
+
ContainerCss (the container bg). White contentClassName covers
|
|
393
|
+
the body when expanded, so the gradient only shows on the
|
|
394
|
+
header bar. White title text + primary-50 summary read cleanly
|
|
395
|
+
on the gradient. */}
|
|
396
|
+
<div className="overflow-hidden rounded-2xl shadow-md">
|
|
397
|
+
<Collapse
|
|
398
|
+
title="Reminders"
|
|
399
|
+
summary={`${REMINDER_ITEMS.length} pending`}
|
|
400
|
+
defaultOpen
|
|
401
|
+
icon={<BellIcon className="h-5 w-5 text-white" />}
|
|
402
|
+
collapseContainerCss="bg-gradient-to-br from-primary-500 via-primary-400 to-violet-500 !border-0"
|
|
403
|
+
notCollapseContainerCss="bg-gradient-to-br from-primary-500 via-primary-400 to-violet-500"
|
|
404
|
+
titleClassName="text-white text-base font-semibold"
|
|
405
|
+
summaryClassName="text-primary-50"
|
|
406
|
+
iconClassName="w-5 h-5 text-white"
|
|
407
|
+
contentClassName="p-0 bg-white border-t border-primary-100"
|
|
408
|
+
content={
|
|
409
|
+
<NewList
|
|
410
|
+
list={REMINDER_ITEMS}
|
|
411
|
+
className="rounded-none border-0 shadow-none"
|
|
431
412
|
/>
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
</p>
|
|
436
|
-
<p className="text-sm text-center md:text-base">{item.desc}</p>
|
|
437
|
-
</div>
|
|
438
|
-
))}
|
|
413
|
+
}
|
|
414
|
+
/>
|
|
415
|
+
</div>
|
|
439
416
|
</div>
|
|
440
|
-
</Container>
|
|
441
417
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
418
|
+
{/* Recent activity — emoji avatars. Full width on desktop to match
|
|
419
|
+
the Progress/Reminders row above. */}
|
|
420
|
+
<div className="mt-4">
|
|
421
|
+
<div className="mb-2 flex items-center justify-between">
|
|
422
|
+
<h2 className="text-sm font-bold uppercase tracking-wide text-gray-500">
|
|
423
|
+
Recent activity
|
|
447
424
|
</h2>
|
|
448
|
-
<
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
Let Us Know Your Details
|
|
455
|
-
</p>
|
|
425
|
+
<button
|
|
426
|
+
type="button"
|
|
427
|
+
className="text-xs font-semibold text-primary-600 hover:text-primary-700"
|
|
428
|
+
>
|
|
429
|
+
View all →
|
|
430
|
+
</button>
|
|
456
431
|
</div>
|
|
457
|
-
<
|
|
458
|
-
B. Property Details
|
|
459
|
-
</p>
|
|
460
|
-
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
|
461
|
-
<Select
|
|
462
|
-
label="Roof Type"
|
|
463
|
-
value="option-a"
|
|
464
|
-
options={[
|
|
465
|
-
{ key: "option-a", label: "Flat Roof" },
|
|
466
|
-
{ key: "option-b", label: "Pitched Roof" },
|
|
467
|
-
{ key: "option-c", label: "Metal Roof" },
|
|
468
|
-
]}
|
|
469
|
-
/>
|
|
470
|
-
<Select
|
|
471
|
-
label="Electricity Tariff"
|
|
472
|
-
value="option-a"
|
|
473
|
-
options={[
|
|
474
|
-
{ key: "option-a", label: "Residential (TNB)" },
|
|
475
|
-
{ key: "option-b", label: "Commercial (TNB)" },
|
|
476
|
-
{ key: "option-c", label: "Industrial (TNB)" },
|
|
477
|
-
]}
|
|
478
|
-
/>
|
|
479
|
-
<Select
|
|
480
|
-
label="Grid Connection"
|
|
481
|
-
value="option-a"
|
|
482
|
-
options={[
|
|
483
|
-
{ key: "option-a", label: "Single Phase" },
|
|
484
|
-
{ key: "option-b", label: "Three Phase" },
|
|
485
|
-
]}
|
|
486
|
-
/>
|
|
487
|
-
<TextInput
|
|
488
|
-
label="Monthly Bill (RM)"
|
|
489
|
-
type="number"
|
|
490
|
-
value="500"
|
|
491
|
-
placeholder="e.g. 500"
|
|
492
|
-
/>
|
|
493
|
-
</div>
|
|
494
|
-
<div className="mt-12 mb-4 text-center">
|
|
495
|
-
<h3 className="text-2xl font-semibold text-primary-800 md:text-3xl">
|
|
496
|
-
Comparison between Solar PV System and Solar + Battery Storage
|
|
497
|
-
</h3>
|
|
498
|
-
<p className="mt-2 text-gray-500 text-sm">
|
|
499
|
-
Based on your inputs, here are the recommended options for you.
|
|
500
|
-
</p>
|
|
501
|
-
</div>
|
|
502
|
-
<Grid
|
|
503
|
-
data={PRODUCT_CARDS}
|
|
504
|
-
loading={false}
|
|
505
|
-
hidePagination
|
|
506
|
-
customGridItems={PRODUCT_GRID_ITEMS}
|
|
507
|
-
itemClassName="!bg-gray-50 shadow-none"
|
|
508
|
-
gridColClassName="gap-6 grid-cols-1 sm:grid-cols-2"
|
|
509
|
-
/>
|
|
510
|
-
</Container>
|
|
511
|
-
</div>
|
|
512
|
-
|
|
513
|
-
<div className="w-full flex flex-col bg-primary-900 lg:flex-row">
|
|
514
|
-
<div className="hidden overflow-hidden lg:w-1/2 lg:block">
|
|
515
|
-
<img
|
|
516
|
-
src="https://images.unsplash.com/photo-1508514177221-188b1cf16e9d?auto=format&fit=crop&w=800&q=80"
|
|
517
|
-
alt="how it works"
|
|
518
|
-
className="w-full h-full object-cover"
|
|
519
|
-
/>
|
|
432
|
+
<NewList list={RECENT_ITEMS} />
|
|
520
433
|
</div>
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
</h1>
|
|
525
|
-
<IconList
|
|
526
|
-
iconList={HOW_IT_WORKS_STEPS.map((step) => ({
|
|
527
|
-
name: step.title,
|
|
528
|
-
content: step.description,
|
|
529
|
-
icon: step.icon,
|
|
530
|
-
}))}
|
|
531
|
-
className="text-white [&_p]:text-white [&_dd]:text-gray-200"
|
|
532
|
-
/>
|
|
434
|
+
|
|
435
|
+
{/* Bottom CTA — Button (type="normal" -> primary-500) */}
|
|
436
|
+
<div className="mt-6 md:mx-auto md:max-w-xs">
|
|
533
437
|
<Button
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
onClick={() => {}}
|
|
538
|
-
className="mt-8 w-full sm:w-fit"
|
|
438
|
+
type="normal"
|
|
439
|
+
text="Get more insights"
|
|
440
|
+
icon={<SparklesIcon className="h-4 w-4" />}
|
|
539
441
|
/>
|
|
540
442
|
</div>
|
|
541
|
-
</div>
|
|
542
|
-
|
|
543
|
-
<Container bgColor="bg-gray-50" className="py-14">
|
|
544
|
-
<h2 className="mb-14 text-3xl font-semibold text-primary-800 text-center md:text-5xl">
|
|
545
|
-
Frequently Asked Questions (FAQ)
|
|
546
|
-
</h2>
|
|
547
|
-
<div className="bg-white rounded-3xl text-primary-900 shadow-xl">
|
|
548
|
-
<FAQContent faqList={FAQ_LIST} />
|
|
549
|
-
</div>
|
|
550
443
|
</Container>
|
|
551
444
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
</div>
|
|
559
|
-
</Container>
|
|
560
|
-
|
|
561
|
-
<Container bgColor="bg-white" className="py-10">
|
|
562
|
-
<ActionSection
|
|
563
|
-
title="Interested to save money? Switch today!"
|
|
564
|
-
buttonText="GET A FREE QUOTE"
|
|
565
|
-
backgroundImgSrc="https://images.unsplash.com/photo-1508514177221-188b1cf16e9d?auto=format&fit=crop&w=1920&q=80"
|
|
566
|
-
backgroundImgStyle={{ bgPosition: "bottom" }}
|
|
567
|
-
onClick={() => {}}
|
|
445
|
+
{/* DESKTOP FOOTER — UI Web FooterSection. Hidden on mobile;
|
|
446
|
+
BottomTab handles thumb-reach nav there. */}
|
|
447
|
+
<div className="hidden md:block">
|
|
448
|
+
<FooterSection
|
|
449
|
+
caption={`© ${TODAY.getFullYear()} Blixify. All rights reserved.`}
|
|
450
|
+
footerList={FOOTER_LIST}
|
|
568
451
|
/>
|
|
569
|
-
</
|
|
452
|
+
</div>
|
|
570
453
|
|
|
571
|
-
<
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
{
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
{ name: "Terms & Conditions", link: "/terms" },
|
|
581
|
-
{ name: "Privacy Policy", link: "/privacy" },
|
|
582
|
-
],
|
|
583
|
-
},
|
|
584
|
-
{
|
|
585
|
-
title: "My Account",
|
|
586
|
-
subList: [
|
|
587
|
-
{ name: "Sign In", link: "/signIn" },
|
|
588
|
-
{ name: "FAQ", link: "/faq" },
|
|
589
|
-
],
|
|
590
|
-
},
|
|
591
|
-
]}
|
|
592
|
-
logoCloud={
|
|
593
|
-
<div>
|
|
594
|
-
<div className="flex items-center space-x-2 mb-3">
|
|
595
|
-
<span className="font-semibold text-gray-900">SolarSave</span>
|
|
596
|
-
</div>
|
|
597
|
-
<p className="text-sm text-gray-500">
|
|
598
|
-
Lorem ipsum dolor sit amet consectetur adipiscing elit.
|
|
599
|
-
</p>
|
|
600
|
-
</div>
|
|
601
|
-
}
|
|
602
|
-
/>
|
|
454
|
+
{/* MOBILE BOTTOM TAB — only on < md. On desktop the footer above
|
|
455
|
+
handles secondary nav; bottom tab would duplicate the header. */}
|
|
456
|
+
<div className="md:hidden">
|
|
457
|
+
<BottomTab
|
|
458
|
+
tabData={BOTTOM_TABS}
|
|
459
|
+
selectedTab="Home"
|
|
460
|
+
bottomTabClassName="fixed bottom-0 left-0 right-0 shadow-lg"
|
|
461
|
+
/>
|
|
462
|
+
</div>
|
|
603
463
|
</div>
|
|
604
464
|
);
|
|
605
|
-
}
|
|
465
|
+
};
|