@salesforce/webapp-template-app-react-sample-b2x-experimental 1.84.0 → 1.85.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.
Files changed (57) hide show
  1. package/dist/.a4drules/skills/{webapp-react-add-component → webapp-react}/SKILL.md +5 -3
  2. package/dist/.a4drules/skills/{webapp-react-add-component → webapp-react}/implementation/header-footer.md +8 -0
  3. package/dist/.a4drules/skills/{webapp-react-add-component → webapp-react}/implementation/page.md +8 -7
  4. package/dist/.a4drules/skills/webapp-ui-ux/SKILL.md +11 -8
  5. package/dist/.a4drules/webapp-react.md +54 -0
  6. package/dist/CHANGELOG.md +16 -0
  7. package/dist/README.md +24 -0
  8. package/dist/force-app/main/default/data/Property_Image__c.json +1 -1
  9. package/dist/force-app/main/default/data/Property_Listing__c.json +1 -1
  10. package/dist/force-app/main/default/data/prepare-import-unique-fields.js +85 -0
  11. package/dist/force-app/main/default/permissionsets/Property_Management_Access.permissionset-meta.xml +0 -7
  12. package/dist/force-app/main/default/webapplications/appreactsampleb2x/index.html +6 -0
  13. package/dist/force-app/main/default/webapplications/appreactsampleb2x/package.json +3 -3
  14. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/applicationApi.ts +9 -9
  15. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/graphql-operations-types.ts +296 -0
  16. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/graphqlClient.ts +12 -7
  17. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/maintenanceRequestApi.ts +50 -38
  18. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/propertyDetailGraphQL.ts +50 -102
  19. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/propertyListingGraphQL.ts +211 -43
  20. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/userApi.ts +43 -0
  21. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/appLayout.tsx +9 -208
  22. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/icons/appliances.svg +13 -0
  23. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/icons/electrical.svg +39 -0
  24. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/icons/hvac.svg +78 -0
  25. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/icons/pest.svg +5 -0
  26. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/icons/plumbing.svg +7 -0
  27. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/assets/icons/zen-logo.svg +5 -0
  28. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/MaintenanceRequestIcon.tsx +46 -0
  29. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/NavMenu.tsx +53 -0
  30. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/PropertyListingCard.tsx +55 -58
  31. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/PropertyMap.tsx +93 -11
  32. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/PropertySearchFilters.tsx +315 -0
  33. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/StatusBadge.tsx +36 -0
  34. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/TopBar.tsx +107 -0
  35. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyAddresses.ts +2 -2
  36. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyListingAmenities.ts +55 -0
  37. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyListingPriceRange.ts +64 -0
  38. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyListingSearch.ts +14 -5
  39. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyMapMarkers.ts +54 -11
  40. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyPrimaryImages.ts +1 -1
  41. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Application.tsx +42 -39
  42. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Contact.tsx +10 -10
  43. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Dashboard.tsx +64 -91
  44. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/HelpCenter.tsx +1 -1
  45. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Home.tsx +19 -9
  46. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Maintenance.tsx +79 -100
  47. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/NotFound.tsx +1 -1
  48. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyDetails.tsx +62 -47
  49. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyListings.tsx +3 -3
  50. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertySearch.tsx +230 -34
  51. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/routes.tsx +10 -1
  52. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/styles/global.css +64 -0
  53. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/geocode.ts +30 -5
  54. package/dist/package.json +1 -1
  55. package/dist/setup-cli.mjs +271 -0
  56. package/package.json +1 -1
  57. /package/dist/.a4drules/skills/{webapp-react-add-component → webapp-react}/implementation/component.md +0 -0
@@ -1,215 +1,16 @@
1
- import { useState, useRef, useEffect, type ComponentType } from "react";
2
- import { Outlet, NavLink, useNavigate } from "react-router";
3
- import { Button } from "./components/ui/button";
4
- import { cn } from "./lib/utils";
5
- import { useAuth } from "./features/authentication/context/AuthContext";
6
- import {
7
- Home,
8
- Search,
9
- BarChart3,
10
- Wrench,
11
- Menu,
12
- Heart,
13
- Bell,
14
- Building2,
15
- Phone,
16
- User,
17
- LogOut,
18
- ChevronDown,
19
- } from "lucide-react";
1
+ import { Outlet } from "react-router";
2
+ import { TopBar } from "@/components/TopBar";
3
+ import { NavMenu } from "@/components/NavMenu";
20
4
 
21
- const SIDEBAR_WIDTH = 200;
22
- const FLOAT_INSET = 20;
23
- const FLOAT_GAP = 20;
24
-
25
- const HEADER_BUTTON =
26
- "cursor-pointer text-primary-foreground transition-colors duration-200 hover:bg-primary-foreground/20";
27
-
28
- interface SidebarLinkProps {
29
- to: string;
30
- label: string;
31
- icon: ComponentType<{ className?: string }>;
32
- end?: boolean;
33
- }
34
-
35
- function SidebarLink({ to, label, icon: Icon, end }: SidebarLinkProps) {
36
- return (
37
- <NavLink
38
- to={to}
39
- end={end}
40
- className={({ isActive }) =>
41
- cn(
42
- "flex min-h-11 w-full flex-shrink-0 cursor-pointer items-center justify-start gap-3 rounded-xl px-3 py-2 text-muted-foreground no-underline transition-colors duration-200",
43
- isActive && "bg-primary/15 text-primary font-medium",
44
- )
45
- }
46
- aria-label={label}
47
- >
48
- <Icon className="size-[22px] shrink-0" aria-hidden />
49
- <span className="text-sm font-medium">{label}</span>
50
- </NavLink>
51
- );
52
- }
53
-
54
- function UserMenu() {
55
- const [open, setOpen] = useState(false);
56
- const ref = useRef<HTMLDivElement>(null);
57
- const navigate = useNavigate();
58
- const { user, logout } = useAuth();
59
-
60
- useEffect(() => {
61
- if (!open) return;
62
- function handleClickOutside(e: MouseEvent) {
63
- if (ref.current && !ref.current.contains(e.target as Node)) {
64
- setOpen(false);
65
- }
66
- }
67
- document.addEventListener("mousedown", handleClickOutside);
68
- return () => document.removeEventListener("mousedown", handleClickOutside);
69
- }, [open]);
70
-
71
- return (
72
- <div className="relative" ref={ref}>
73
- <button
74
- type="button"
75
- onClick={() => setOpen((o) => !o)}
76
- className={cn(
77
- "flex items-center gap-1.5 rounded-md border-none bg-transparent px-2 py-1.5 text-sm font-medium",
78
- HEADER_BUTTON,
79
- )}
80
- aria-haspopup="true"
81
- aria-expanded={open}
82
- >
83
- {user!.name}
84
- <ChevronDown
85
- className={cn("size-4 transition-transform duration-200", open && "rotate-180")}
86
- aria-hidden
87
- />
88
- </button>
89
- {open && (
90
- <div
91
- className="absolute right-0 top-full z-50 mt-1.5 w-44 overflow-hidden rounded-lg border border-border bg-card py-1 shadow-lg"
92
- role="menu"
93
- >
94
- <button
95
- type="button"
96
- role="menuitem"
97
- className="flex w-full cursor-pointer items-center gap-2 border-none bg-transparent px-3 py-2 text-left text-sm text-foreground transition-colors duration-150 hover:bg-muted"
98
- onClick={() => {
99
- setOpen(false);
100
- navigate("/profile");
101
- }}
102
- >
103
- <User className="size-4" aria-hidden />
104
- Edit Profile
105
- </button>
106
- <button
107
- type="button"
108
- role="menuitem"
109
- className="flex w-full cursor-pointer items-center gap-2 border-none bg-transparent px-3 py-2 text-left text-sm text-destructive transition-colors duration-150 hover:bg-destructive/10"
110
- onClick={() => {
111
- setOpen(false);
112
- logout();
113
- }}
114
- >
115
- <LogOut className="size-4" aria-hidden />
116
- Log Out
117
- </button>
118
- </div>
119
- )}
120
- </div>
121
- );
122
- }
123
-
124
- function Sidebar({ isAuthenticated }: { isAuthenticated: boolean }) {
5
+ export default function AppLayout() {
125
6
  return (
126
- <nav
127
- className="absolute bottom-5 left-5 top-5 z-10 flex w-[200px] flex-col items-start gap-1 overflow-hidden rounded-2xl border border-border bg-card p-4 py-2 shadow-md"
128
- aria-label="Main navigation"
129
- >
130
- <SidebarLink to="/" label="Home" icon={Home} end />
131
- <SidebarLink to="/properties" label="Property Search" icon={Search} />
132
- {isAuthenticated && (
133
- <>
134
- <SidebarLink to="/dashboard" label="Dashboard" icon={BarChart3} />
135
- <SidebarLink to="/maintenance" label="Maintenance" icon={Wrench} />
136
- </>
137
- )}
138
- <SidebarLink to="/contact" label="Contact" icon={Phone} />
139
- </nav>
140
- );
141
- }
7
+ <div className="flex h-screen flex-col">
8
+ <TopBar />
142
9
 
143
- export default function AppLayout() {
144
- const [navHidden, setNavHidden] = useState(false);
145
- const { isAuthenticated, loading, user } = useAuth();
10
+ <div className="flex flex-1 overflow-hidden">
11
+ <NavMenu />
146
12
 
147
- return (
148
- <div className="flex min-h-screen flex-col">
149
- <header
150
- className="flex shrink-0 items-center justify-between bg-primary px-6 py-3 text-primary-foreground"
151
- role="banner"
152
- >
153
- <div className="flex items-center gap-3">
154
- <Button
155
- type="button"
156
- variant="ghost"
157
- size="icon"
158
- className={cn("min-h-11 min-w-11", HEADER_BUTTON)}
159
- aria-label={navHidden ? "Show menu" : "Hide menu"}
160
- onClick={() => setNavHidden((h) => !h)}
161
- >
162
- <Menu className="size-6" aria-hidden />
163
- </Button>
164
- <div className="flex size-8 items-center justify-center">
165
- <Building2 className="size-7" aria-hidden />
166
- </div>
167
- <span className="text-xl font-semibold tracking-wide">ZENLEASE</span>
168
- </div>
169
- <div className="flex items-center gap-4">
170
- <Button
171
- type="button"
172
- variant="ghost"
173
- size="icon"
174
- className={HEADER_BUTTON}
175
- aria-label="Favorites"
176
- >
177
- <Heart className="size-5" aria-hidden />
178
- </Button>
179
- <Button
180
- type="button"
181
- variant="ghost"
182
- size="icon"
183
- className={HEADER_BUTTON}
184
- aria-label="Notifications"
185
- >
186
- <Bell className="size-5" aria-hidden />
187
- </Button>
188
- {loading ? (
189
- <span className="text-sm font-medium text-primary-foreground/70" aria-hidden>
190
-
191
- </span>
192
- ) : isAuthenticated && user ? (
193
- <UserMenu />
194
- ) : (
195
- <NavLink
196
- to="/login"
197
- className="rounded-md px-2 py-1.5 text-sm font-medium text-primary-foreground no-underline transition-colors duration-200 hover:bg-primary-foreground/20 hover:text-primary-foreground/90"
198
- >
199
- Sign Up / Sign In
200
- </NavLink>
201
- )}
202
- </div>
203
- </header>
204
- <div className="relative flex min-h-0 flex-1">
205
- {!navHidden && <Sidebar isAuthenticated={isAuthenticated} />}
206
- <main
207
- className="min-h-full flex-1 overflow-auto bg-muted/40 p-6 transition-[margin-left] duration-200 ease-[cubic-bezier(0.4,0,0.2,1)]"
208
- style={{
209
- marginLeft: navHidden ? 0 : FLOAT_INSET + SIDEBAR_WIDTH + FLOAT_GAP,
210
- }}
211
- role="main"
212
- >
13
+ <main className="flex-1 overflow-auto bg-gray-50 p-8" role="main">
213
14
  <Outlet />
214
15
  </main>
215
16
  </div>
@@ -0,0 +1,13 @@
1
+ <?xml version='1.0' encoding='iso-8859-1'?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg fill="currentColor" height="800px" width="800px" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 463 463" xmlns:xlink="http://www.w3.org/1999/xlink" enable-background="new 0 0 463 463">
4
+ <g>
5
+ <path d="m367.5,0h-272c-21.78,0-39.5,17.72-39.5,39.5v368c0,10.336 6.71,19.128 16,22.266v9.734c0,12.958 10.542,23.5 23.5,23.5h272c12.958,0 23.5-10.542 23.5-23.5v-9.734c9.29-3.138 16-11.93 16-22.266v-368c0-21.78-17.72-39.5-39.5-39.5zm-272,15h272c13.51,0 24.5,10.991 24.5,24.5v56.5h-321v-56.5c0-13.509 10.99-24.5 24.5-24.5zm272,433h-272c-4.687,0-8.5-3.813-8.5-8.5v-8.5h289v8.5c0,4.687-3.813,8.5-8.5,8.5zm16-32h-304c-4.687,0-8.5-3.813-8.5-8.5v-296.5h321v296.5c0,4.687-3.813,8.5-8.5,8.5z"/>
6
+ <path d="M231.5,136C161.196,136,104,193.196,104,263.5S161.196,391,231.5,391S359,333.804,359,263.5S301.804,136,231.5,136z M231.5,376C169.468,376,119,325.533,119,263.5S169.468,151,231.5,151S344,201.467,344,263.5S293.532,376,231.5,376z"/>
7
+ <path d="m279.5,79c12.958,0 23.5-10.542 23.5-23.5s-10.542-23.5-23.5-23.5-23.5,10.542-23.5,23.5 10.542,23.5 23.5,23.5zm0-32c4.687,0 8.5,3.813 8.5,8.5s-3.813,8.5-8.5,8.5-8.5-3.813-8.5-8.5 3.813-8.5 8.5-8.5z"/>
8
+ <path d="m343.5,79c12.958,0 23.5-10.542 23.5-23.5s-10.542-23.5-23.5-23.5-23.5,10.542-23.5,23.5 10.542,23.5 23.5,23.5zm0-32c4.687,0 8.5,3.813 8.5,8.5s-3.813,8.5-8.5,8.5-8.5-3.813-8.5-8.5 3.813-8.5 8.5-8.5z"/>
9
+ <path d="m111.5,79h104c8.547,0 15.5-6.953 15.5-15.5v-16c0-8.547-6.953-15.5-15.5-15.5h-104c-8.547,0-15.5,6.953-15.5,15.5v16c0,8.547 6.953,15.5 15.5,15.5zm-.5-31.5c0-0.276 0.225-0.5 0.5-0.5h104c0.275,0 0.5,0.224 0.5,0.5v16c0,0.276-0.225,0.5-0.5,0.5h-104c-0.275,0-0.5-0.224-0.5-0.5v-16z"/>
10
+ <path d="m231.5,168c-52.659,0-95.5,42.841-95.5,95.5s42.841,95.5 95.5,95.5 95.5-42.841 95.5-95.5-42.841-95.5-95.5-95.5zm0,176c-44.388,0-80.5-36.112-80.5-80.5s36.112-80.5 80.5-80.5 80.5,36.112 80.5,80.5-36.112,80.5-80.5,80.5z"/>
11
+ <path d="m231.5,200c-4.143,0-7.5,3.358-7.5,7.5s3.357,7.5 7.5,7.5c26.743,0 48.5,21.757 48.5,48.5 0,4.142 3.357,7.5 7.5,7.5s7.5-3.358 7.5-7.5c0-35.014-28.486-63.5-63.5-63.5z"/>
12
+ </g>
13
+ </svg>
@@ -0,0 +1,39 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg fill="currentColor" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" xml:space="preserve">
4
+ <g>
5
+ <g>
6
+ <path d="M408.49,193.586c-38.366-34.559-88.566-54.508-142.291-56.752V10.199C266.199,4.566,261.633,0,256,0
7
+ c-5.633,0-10.199,4.566-10.199,10.199v126.634c-53.725,2.245-103.925,22.193-142.291,56.752
8
+ c-41.083,37.008-63.708,86.297-63.708,138.79c0,5.633,4.566,10.199,10.199,10.199h118.284
9
+ c5.513,39.678,42.747,70.459,87.715,70.459s82.202-30.781,87.715-70.459h118.284c5.633,0,10.199-4.566,10.199-10.199
10
+ C472.199,279.883,449.573,230.594,408.49,193.586z M256,392.636c-33.583,0-61.56-21.674-67.047-50.061h134.093
11
+ C317.56,370.961,289.583,392.636,256,392.636z M60.527,322.177c5.917-91.967,91.33-165.163,195.473-165.163
12
+ s189.556,73.196,195.473,165.163H60.527z"/>
13
+ </g>
14
+ </g>
15
+ <g>
16
+ <g>
17
+ <path d="M254.924,429.21c-5.633,0-10.199,4.566-10.199,10.199v62.392c0,5.633,4.566,10.199,10.199,10.199
18
+ c5.633,0,10.199-4.566,10.199-10.199v-62.392C265.123,433.776,260.557,429.21,254.924,429.21z"/>
19
+ </g>
20
+ </g>
21
+ <g>
22
+ <g>
23
+ <path d="M381.687,445.438l-2.481-2.482c-3.983-3.983-10.441-3.983-14.425,0c-3.983,3.983-3.983,10.441,0,14.425l2.482,2.482
24
+ c1.992,1.992,4.602,2.987,7.212,2.987s5.221-0.995,7.212-2.987C385.67,455.88,385.67,449.422,381.687,445.438z"/>
25
+ </g>
26
+ </g>
27
+ <g>
28
+ <g>
29
+ <path d="M356.8,420.55l-19.229-19.23c-3.983-3.983-10.441-3.983-14.425,0c-3.983,3.983-3.983,10.441,0,14.425l19.23,19.23
30
+ c1.992,1.992,4.602,2.987,7.212,2.987s5.221-0.995,7.212-2.987C360.783,430.992,360.783,424.534,356.8,420.55z"/>
31
+ </g>
32
+ </g>
33
+ <g>
34
+ <g>
35
+ <path d="M185.441,404.363c-3.982-3.983-10.44-3.983-14.424,0L126.9,448.48c-3.983,3.983-3.983,10.441,0,14.425
36
+ c1.992,1.992,4.602,2.987,7.212,2.987s5.221-0.995,7.212-2.987l44.118-44.118C189.424,414.805,189.424,408.347,185.441,404.363z"/>
37
+ </g>
38
+ </g>
39
+ </svg>
@@ -0,0 +1,78 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg fill="currentColor" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" xml:space="preserve">
4
+ <g>
5
+ <g>
6
+ <path d="M482.815,93.937H29.185C13.093,93.937,0,107.029,0,123.123v265.755c0,16.093,13.093,29.186,29.185,29.186h453.63
7
+ c16.092,0,29.185-13.093,29.185-29.186V123.123C512,107.029,498.907,93.937,482.815,93.937z M482.815,397.665H29.185
8
+ c-4.845,0-8.787-3.942-8.787-8.788V123.123c0-4.845,3.942-8.788,8.787-8.788h453.63c4.845,0,8.787,3.942,8.787,8.788v265.755h0
9
+ C491.602,393.722,487.66,397.665,482.815,397.665z"/>
10
+ </g>
11
+ </g>
12
+ <g>
13
+ <g>
14
+ <path d="M165.227,138.709c-64.674,0-117.291,52.617-117.291,117.291s52.617,117.291,117.291,117.291S282.518,320.674,282.518,256
15
+ S229.901,138.709,165.227,138.709z M165.227,352.892c-53.427,0-96.892-43.466-96.892-96.892c0-53.426,43.466-96.892,96.892-96.892
16
+ c53.426,0,96.892,43.466,96.892,96.892C262.12,309.427,218.654,352.892,165.227,352.892z"/>
17
+ </g>
18
+ </g>
19
+ <g>
20
+ <g>
21
+ <path d="M252.137,262.786c-4.959-7.802-13.735-11.878-22.893-10.65c-11.264,1.517-22.867,0.816-33.891-2.002
22
+ c-1.243-5.677-4.005-10.79-7.84-14.892c1.022-2.523,2.468-4.881,4.257-6.924c7.147-8.16,11.081-18.629,11.081-29.475
23
+ c0-16.945-13.258-30.862-30.184-31.684l-24.291-1.179c-9.244-0.453-17.627,4.364-21.904,12.56
24
+ c-4.276,8.196-3.424,17.833,2.224,25.151c6.784,8.789,11.772,18.574,14.875,29.144c-4.704,4.049-8.212,9.444-9.922,15.585
25
+ c-2.285,0.198-4.6,0.078-6.844-0.367c-10.64-2.107-21.672-0.283-31.066,5.141c-14.675,8.472-20.099,26.913-12.347,41.981
26
+ l11.124,21.627c4.07,7.911,11.968,12.71,20.788,12.71c0.346,0,0.694-0.007,1.041-0.021c9.236-0.395,17.155-5.951,20.67-14.502
27
+ c4.25-10.345,10.288-19.619,17.974-27.634c2.942,0.902,6.063,1.389,9.298,1.389c2.924,0,5.754-0.405,8.446-1.147
28
+ c1.567,2.072,2.812,4.394,3.646,6.848c3.495,10.269,10.593,18.911,19.985,24.333c4.998,2.885,10.461,4.264,15.855,4.264
29
+ c10.443,0,20.627-5.167,26.675-14.561l13.167-20.447C257.068,280.262,257.096,270.587,252.137,262.786z M118.148,307.238
30
+ c-0.608,1.481-1.788,1.836-2.67,1.874c-0.882,0.035-2.089-0.215-2.821-1.639l-11.124-21.626
31
+ c-2.767-5.379-0.83-11.962,4.407-14.986c5.111-2.951,11.115-3.943,16.901-2.798c3.914,0.775,7.946,1.007,11.937,0.719
32
+ c0.934,2.318,2.133,4.501,3.56,6.513C129.792,284.674,123.018,295.385,118.148,307.238z M164.289,268.347
33
+ c-6.29,0-11.408-5.118-11.408-11.408s5.118-11.407,11.408-11.407s11.407,5.117,11.407,11.407S170.579,268.347,164.289,268.347z
34
+ M176.422,214.879c-2.8,3.198-5.136,6.819-6.93,10.688c-1.694-0.28-3.431-0.432-5.204-0.432c-0.578,0-1.151,0.017-1.723,0.048
35
+ c-3.845-12.279-9.783-23.67-17.721-33.954c-0.978-1.267-0.697-2.466-0.288-3.25c0.409-0.784,1.206-1.696,2.829-1.623l24.291,1.179
36
+ c6.042,0.294,10.774,5.261,10.774,11.309C182.452,204.744,180.31,210.439,176.422,214.879z M234.912,276.991l-13.168,20.448
37
+ c-3.273,5.085-9.942,6.699-15.181,3.676c-5.111-2.951-8.972-7.652-10.873-13.239c-1.421-4.174-3.49-8.141-6.072-11.73
38
+ c1.318-1.734,2.462-3.605,3.407-5.59c12.734,2.905,26.019,3.537,38.94,1.798c1.581-0.216,2.482,0.627,2.957,1.374
39
+ C235.396,274.474,235.779,275.645,234.912,276.991z"/>
40
+ </g>
41
+ </g>
42
+ <g>
43
+ <g>
44
+ <path d="M456.924,141.769H321.275c-5.632,0-10.199,4.566-10.199,10.199s4.567,10.199,10.199,10.199h135.649
45
+ c5.632,0,10.199-4.566,10.199-10.199S462.556,141.769,456.924,141.769z"/>
46
+ </g>
47
+ </g>
48
+ <g>
49
+ <g>
50
+ <path d="M456.924,193.785H321.275c-5.632,0-10.199,4.566-10.199,10.199s4.567,10.199,10.199,10.199h135.649
51
+ c5.632,0,10.199-4.566,10.199-10.199S462.556,193.785,456.924,193.785z"/>
52
+ </g>
53
+ </g>
54
+ <g>
55
+ <g>
56
+ <path d="M456.924,245.801H321.275c-5.632,0-10.199,4.566-10.199,10.199c0,5.633,4.567,10.199,10.199,10.199h135.649
57
+ c5.632,0,10.199-4.566,10.199-10.199C467.124,250.367,462.556,245.801,456.924,245.801z"/>
58
+ </g>
59
+ </g>
60
+ <g>
61
+ <g>
62
+ <path d="M456.924,297.817H321.275c-5.632,0-10.199,4.566-10.199,10.199c0,5.633,4.567,10.199,10.199,10.199h135.649
63
+ c5.632,0,10.199-4.566,10.199-10.199C467.124,302.383,462.556,297.817,456.924,297.817z"/>
64
+ </g>
65
+ </g>
66
+ <g>
67
+ <g>
68
+ <path d="M416.128,349.833h-94.853c-5.632,0-10.199,4.566-10.199,10.199c0,5.633,4.567,10.199,10.199,10.199h94.853
69
+ c5.632,0,10.199-4.566,10.199-10.199C426.327,354.399,421.76,349.833,416.128,349.833z"/>
70
+ </g>
71
+ </g>
72
+ <g>
73
+ <g>
74
+ <path d="M456.924,349.833h-5.1c-5.632,0-10.199,4.566-10.199,10.199c0,5.633,4.567,10.199,10.199,10.199h5.1
75
+ c5.632,0,10.199-4.566,10.199-10.199C467.124,354.399,462.556,349.833,456.924,349.833z"/>
76
+ </g>
77
+ </g>
78
+ </svg>
@@ -0,0 +1,5 @@
1
+ <?xml version='1.0' encoding='iso-8859-1'?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg fill="currentColor" height="800px" width="800px" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 463 463" xmlns:xlink="http://www.w3.org/1999/xlink" enable-background="new 0 0 463 463">
4
+ <path d="m355.256,145.405c-34.083-19.724-60.085-50.573-73.704-87.254 4.229-2.018 8.958-3.151 13.947-3.151h15.971c0.008,0 0.015,0.002 0.023,0.002 0.051,0 0.102-0.008 0.153-0.009 0.234-0.005 0.465-0.017 0.693-0.042 0.079-0.009 0.157-0.025 0.236-0.036 0.216-0.031 0.43-0.07 0.639-0.119 0.034-0.008 0.069-0.011 0.103-0.019l25.941-6.485c6.912-1.729 11.74-7.912 11.74-15.037v-11.509c0-7.125-4.828-13.309-11.741-15.037l-25.94-6.485c-0.04-0.01-0.081-0.014-0.121-0.024-0.146-0.034-0.293-0.062-0.442-0.087-0.11-0.019-0.221-0.037-0.331-0.051-0.124-0.015-0.25-0.026-0.376-0.035-0.136-0.01-0.271-0.018-0.406-0.021-0.047,2.45463e-15-0.093-0.006-0.142-0.006h-98.745c-10.551,0-20.471,4.109-27.931,11.569l-56.97,56.971c-4.449,4.449-5.768,11.079-3.36,16.892s8.028,9.568 14.32,9.568h29.187v9h-0.5c-8.547,0-15.5,6.953-15.5,15.5v22.273c0,10.239-2.784,20.291-8.052,29.069l-20.639,34.398c-12.632,21.054-19.31,45.161-19.31,69.714v132.546c0,30.603 24.897,55.5 55.5,55.5h96c30.603,0 55.5-24.897 55.5-55.5v-132.546c0-24.553-6.677-48.66-19.31-69.714l-20.639-34.398c-5.268-8.778-8.052-18.83-8.052-29.069v-22.273c0-8.547-6.953-15.5-15.5-15.5h-0.5v-9h8.5c4.142,0 7.5-3.358 7.5-7.5 0-7.219 2.369-13.893 6.365-19.292 15.273,37.82 42.778,69.577 78.378,90.18 1.183,0.685 2.475,1.01 3.75,1.01 2.589,0 5.108-1.343 6.498-3.745 2.076-3.586 0.852-8.174-2.733-10.248zm-19.257-123.659v11.508c0,0.23-0.156,0.43-0.378,0.485l-16.622,4.155v-20.788l16.621,4.155c0.224,0.055 0.379,0.255 0.379,0.485zm-199.828,191.212l20.639-34.398c5.073-8.455 8.32-17.844 9.585-27.56h33.604v144.037c-1.835-0.565-3.396-1.343-5.197-2.244-4.268-2.135-9.579-4.792-19.346-4.792-9.765,0-15.076,2.658-19.343,4.793-3.721,1.862-6.409,3.207-12.631,3.207-6.225,0-8.914-1.345-12.636-3.208-3.02-1.511-6.572-3.279-11.847-4.186v-13.652c-4.26326e-14-21.836 5.938-43.274 17.172-61.997zm71.328,186.042c4.687,0 8.5,3.813 8.5,8.5v8.5h-17v-8.5c0-4.687 3.813-8.5 8.5-8.5zm48,49h-96c-22.332,0-40.5-18.168-40.5-40.5v-103.517c1.808,0.564 3.356,1.334 5.136,2.225 4.268,2.135 9.58,4.792 19.347,4.792 9.766,0 15.076-2.658 19.344-4.793 3.721-1.862 6.409-3.207 12.63-3.207 6.224,0 8.912,1.345 12.634,3.208 3.032,1.517 6.6,3.293 11.909,4.196v73.096c0,0.575 0.071,1.132 0.193,1.669-9.39,3.081-16.193,11.924-16.193,22.331v16c0,4.142 3.358,7.5 7.5,7.5h32c4.142,0 7.5-3.358 7.5-7.5v-16c0-10.407-6.803-19.25-16.193-22.331 0.122-0.537 0.193-1.094 0.193-1.669v-73.117c5.241-0.91 8.776-2.671 11.785-4.176 3.723-1.862 6.413-3.208 12.639-3.208 6.241,0 8.937,1.346 12.668,3.21 4.273,2.134 9.592,4.79 19.37,4.79s15.097-2.656 19.371-4.79c1.791-0.895 3.347-1.668 5.168-2.233v103.524c-0.001,22.332-18.169,40.5-40.501,40.5zm23.329-235.042c11.234,18.724 17.172,40.162 17.172,61.997v13.65c-5.286,0.906-8.844,2.674-11.87,4.185-3.731,1.864-6.427,3.21-12.668,3.21-6.241,0-8.937-1.346-12.668-3.21-4.273-2.134-9.592-4.79-19.37-4.79-9.768,0-15.081,2.657-19.349,4.792-1.76,0.881-3.293,1.643-5.074,2.205v-143.997h33.604c1.265,9.715 4.512,19.104 9.585,27.56l20.638,34.398zm-30.829-93.458v16.5h-81v-16.5c0-0.276 0.224-0.5 0.5-0.5h80c0.276,0 0.5,0.224 0.5,0.5zm-16-15.5h-49v-9h49v9zm16.592-24h-109.778c-0.179,0-0.334,0-0.462-0.309-0.127-0.309-0.018-0.418 0.108-0.545l56.971-56.971c4.628-4.627 10.78-7.175 17.324-7.175h91.245v25h-8.5c-23.64,0-43.302,17.359-46.908,40z"/>
5
+ </svg>
@@ -0,0 +1,7 @@
1
+ <?xml version='1.0' encoding='iso-8859-1'?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg fill="currentColor" height="800px" width="800px" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 463 463" xmlns:xlink="http://www.w3.org/1999/xlink" enable-background="new 0 0 463 463">
4
+ <g>
5
+ <path d="m447,341.234v-17.734c0-79.126-64.374-143.5-143.5-143.5h-113.734c-3.138-9.29-11.93-16-22.266-16-4.687,0-8.5-3.813-8.5-8.5v-8.5h0.5c4.142,0 7.5-3.358 7.5-7.5s-3.358-7.5-7.5-7.5h-16.5v-27.077l47.496,9.543c1.786,0.357 3.582,0.533 5.365,0.533 6.219,0 12.28-2.137 17.192-6.165 6.321-5.182 9.947-12.842 9.947-21.016s-3.625-15.833-9.946-21.016c-6.322-5.183-14.544-7.236-22.558-5.632l-50.889,10.177c-4.123-6.796-11.593-11.347-20.107-11.347s-15.984,4.551-20.107,11.348l-50.889-10.178c-8.013-1.602-16.237,0.45-22.558,5.632-6.321,5.182-9.946,12.842-9.946,21.016s3.625,15.834 9.947,21.016c4.913,4.028 10.975,6.166 17.192,6.166 1.781,0 3.575-0.176 5.357-0.532l47.504-9.443v26.975h-16.5c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5 7.5,7.5h0.5v8.5c0,4.687-3.813,8.5-8.5,8.5-10.336,0-19.128,6.71-22.266,16h-25.734c-12.958,0-23.5,10.542-23.5,23.5v64c0,12.958 10.542,23.5 23.5,23.5h25.734c3.138,9.29 11.93,16 22.266,16h96c10.336,0 19.128-6.71 22.266-16h113.734c17.92,0 32.5,14.58 32.5,32.5v17.734c-9.29,3.138-16,11.93-16,22.266v16c0,12.958 10.542,23.5 23.5,23.5h96c12.958,0 23.5-10.542 23.5-23.5v-16c0-10.336-6.71-19.128-16-22.266zm-253.563-265.355c3.589-0.719 7.274,0.201 10.107,2.523 2.832,2.322 4.456,5.753 4.456,9.416s-1.625,7.094-4.457,9.416c-2.832,2.322-6.516,3.242-10.1,2.524l-50.443-10.135v-3.657l50.437-10.087zm-73.937-.879c4.687,0 8.5,3.813 8.5,8.5v16.5h-17v-16.5c0-4.687 3.813-8.5 8.5-8.5zm-73.937,24.757c-3.592,0.719-7.275-0.202-10.106-2.523-2.832-2.322-4.457-5.754-4.457-9.416s1.624-7.094 4.456-9.416c2.832-2.322 6.515-3.243 10.107-2.523l50.437,10.088v3.765l-50.437,10.025zm65.437,15.243h17v17h-17v-17zm-96,152.5v-64c0-4.687 3.813-8.5 8.5-8.5h24.5v81h-24.5c-4.687,0-8.5-3.813-8.5-8.5zm161,16c0,4.687-3.813,8.5-8.5,8.5h-96c-4.68,0-8.488-3.803-8.499-8.481 0-0.007 0.001-0.013 0.001-0.019 0-0.013-0.002-0.026-0.002-0.039v-95.923c0-0.013 0.002-0.026 0.002-0.039 0-0.007-0.001-0.013-0.001-0.019 0.011-4.677 3.819-8.48 8.499-8.48 12.958,0 23.5-10.542 23.5-23.5v-8.5h49v8.5c0,12.958 10.542,23.5 23.5,23.5 4.687,0 8.5,3.813 8.5,8.5v96zm127.5-7.5h-112.5v-81h112.5c70.855,0 128.5,57.645 128.5,128.5v16.5h-81v-16.5c0-26.191-21.309-47.5-47.5-47.5zm144.5,103.5c0,4.687-3.813,8.5-8.5,8.5h-96c-4.687,0-8.5-3.813-8.5-8.5v-16c0-4.687 3.813-8.5 8.5-8.5h96c4.687,0 8.5,3.813 8.5,8.5v16z"/>
6
+ </g>
7
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="40" height="40" rx="8" fill="black" fill-opacity="0.2"/>
3
+ <path d="M10 31V16.3333C13.4868 16.6744 19.9209 18.0046 20.3194 20.5969C20.4604 21.5144 20.45 31 20.45 31M13.4972 31L13.7641 13.9457C13.7641 10.1 25.95 9 25.95 9V31" stroke="white" stroke-width="1.5"/>
4
+ <path d="M29.2494 31.0001V23.4681C25.2137 22.8637 21.6875 23.9923 19.3778 25.2149C18.3982 25.7334 17.6374 26.2688 17.1494 26.6961V31.0001" stroke="white" stroke-width="1.5"/>
5
+ </svg>
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Renders a type-specific icon for maintenance requests (same approach as b2e MaintenanceTable).
3
+ * Uses imported SVG assets; fallback to 🔧 when type has no icon.
4
+ */
5
+ import PlumbingIcon from "@/assets/icons/plumbing.svg";
6
+ import HVACIcon from "@/assets/icons/hvac.svg";
7
+ import ElectricalIcon from "@/assets/icons/electrical.svg";
8
+ import AppliancesIcon from "@/assets/icons/appliances.svg";
9
+ import PestIcon from "@/assets/icons/pest.svg";
10
+
11
+ const issueIcons: Record<string, string> = {
12
+ Plumbing: PlumbingIcon,
13
+ HVAC: HVACIcon,
14
+ Electrical: ElectricalIcon,
15
+ Appliance: AppliancesIcon,
16
+ Pest: PestIcon,
17
+ };
18
+
19
+ const issueIconColors: Record<string, string> = {
20
+ Plumbing: "bg-teal-100",
21
+ HVAC: "bg-teal-100",
22
+ Electrical: "bg-teal-100",
23
+ Appliance: "bg-teal-100",
24
+ Pest: "bg-teal-100",
25
+ };
26
+
27
+ export function MaintenanceRequestIcon({ type }: { type: string | null }) {
28
+ const issueType = type?.trim() ?? "";
29
+ const iconSrc = issueIcons[issueType];
30
+ const bgClass = issueIconColors[issueType] ?? "bg-teal-100";
31
+
32
+ return (
33
+ <div
34
+ className={`flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-lg ${bgClass}`}
35
+ aria-hidden
36
+ >
37
+ {iconSrc ? (
38
+ <img src={iconSrc} alt={issueType || "Request"} className="h-6 w-6" />
39
+ ) : (
40
+ <span className="text-2xl" aria-hidden>
41
+ 🔧
42
+ </span>
43
+ )}
44
+ </div>
45
+ );
46
+ }
@@ -0,0 +1,53 @@
1
+ import { Link, useLocation } from "react-router";
2
+ import { Home, Search, BarChart3, Wrench, Phone, type LucideIcon } from "lucide-react";
3
+
4
+ interface NavItem {
5
+ path: string;
6
+ icon: LucideIcon;
7
+ label: string;
8
+ }
9
+
10
+ const navItems: NavItem[] = [
11
+ { path: "/", icon: Home, label: "Home" },
12
+ { path: "/dashboard", icon: BarChart3, label: "Dashboard" },
13
+ { path: "/properties", icon: Search, label: "Property Search" },
14
+ { path: "/maintenance/requests", icon: Wrench, label: "Maintenance Requests" },
15
+ { path: "/contact", icon: Phone, label: "Contact Us" },
16
+ ];
17
+
18
+ export function NavMenu() {
19
+ const location = useLocation();
20
+
21
+ const isActive = (path: string) => {
22
+ if (path === "/") return location.pathname === "/";
23
+ return location.pathname.startsWith(path);
24
+ };
25
+
26
+ return (
27
+ <nav
28
+ className="flex w-24 flex-col border-r border-gray-200 bg-white py-8"
29
+ aria-label="Main navigation"
30
+ >
31
+ {navItems.map((item) => {
32
+ const Icon = item.icon;
33
+ const active = isActive(item.path);
34
+ return (
35
+ <Link
36
+ key={item.path}
37
+ to={item.path}
38
+ className={`flex flex-col items-center justify-center gap-2 px-2 py-4 transition-colors ${
39
+ active
40
+ ? "border-l-4 border-teal-700 bg-teal-100 text-teal-700"
41
+ : "text-gray-600 hover:bg-gray-100 hover:text-gray-900"
42
+ }`}
43
+ title={item.label}
44
+ aria-label={item.label}
45
+ >
46
+ <Icon className="size-6 shrink-0" aria-hidden />
47
+ <span className="text-center text-xs font-medium leading-tight">{item.label}</span>
48
+ </Link>
49
+ );
50
+ })}
51
+ </nav>
52
+ );
53
+ }