PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
yatra / resources / js / lib / sidebar-menu-defaults.ts

sidebar-menu-defaults.ts in Yatra – Travel Booking & Tour Operator Software 3.0.16, at resources/js/lib/sidebar-menu-defaults.ts

343 lines 9.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Shared sidebar menu definitions for the Yatra React admin.
3 *
4 * The single source of truth for menu slugs, default labels, and default
5 * icon names. Layout.tsx renders from this list (plus per-item overrides
6 * from window.yatraAdmin.brandMenuOverrides); the White Label settings
7 * page reads the same list to generate the customization form.
8 *
9 * Icons are referenced by Lucide name as strings so they can round-trip
10 * through saved settings; the runtime icon map lives in ./icon-map.ts.
11 */
12
13 export interface SubmenuItemDefault {
14 /** Combined "parent.tab" key — unique across the whole menu tree. */
15 key: string;
16 /** Owning top-level slug. */
17 parentSlug: string;
18 /** Tab segment used by Layout.tsx for URL routing. */
19 tab: string;
20 label: string;
21 iconName: string;
22 }
23
24 export interface MenuItemDefault {
25 slug: string;
26 label: string;
27 iconName: string;
28 submenu?: SubmenuItemDefault[];
29 }
30
31 /**
32 * Icon override may be either:
33 * - a Lucide name string (legacy / curated Lucide dropdown)
34 * - an IconPickerValue (Yatra-library icon, FontAwesome, or image upload)
35 *
36 * The sidebar renderer (MenuIcon) handles all three shapes; existing
37 * stored data keeps working without migration.
38 */
39 import type { IconPickerValue } from "./icon-picker-types";
40
41 export type MenuIconValue = string | IconPickerValue;
42
43 /**
44 * Per-item override. `parent` reparents the item under a different
45 * top-level slug ("" / undefined = keep at top-level). Cross-parent
46 * moves preserve the original click URL — the sidebar tree is purely
47 * visual; routes still resolve via the original subpage/tab pair.
48 */
49 export interface MenuItemOverride {
50 label?: string;
51 icon?: MenuIconValue;
52 hidden?: boolean;
53 parent?: string;
54 }
55
56 /**
57 * Ordered children list per parent. The empty-string key holds the
58 * top-level order; each top-level slug key holds that parent's ordered
59 * children. Anything not listed falls back to its default position.
60 */
61 export type MenuOrderMap = Record<string, string[]>;
62
63 /**
64 * Submenu overrides are keyed by `{parentSlug}.{tab}` and currently
65 * support label/icon/hidden — cross-parent moves are not supported yet
66 * because the React App.tsx routing pins each submenu to its parent.
67 */
68 export type MenuOverrides = Record<string, MenuItemOverride>;
69
70 /**
71 * All known top-level sidebar items. Includes Pro-gated entries (their
72 * visibility is still gated by isProPluginActive/isModuleActive at render
73 * time) so customers can pre-configure them before enabling those modules.
74 */
75 export const DEFAULT_MENU_ITEMS: MenuItemDefault[] = [
76 { slug: "dashboard", label: "Dashboard", iconName: "LayoutDashboard" },
77 {
78 slug: "trips",
79 label: "Trips",
80 iconName: "MapPin",
81 submenu: [
82 {
83 key: "trips.all",
84 parentSlug: "trips",
85 tab: "all",
86 label: "All Trips",
87 iconName: "List",
88 },
89 {
90 key: "trips.activities",
91 parentSlug: "trips",
92 tab: "activities",
93 label: "Activities",
94 iconName: "Activity",
95 },
96 {
97 key: "trips.destinations",
98 parentSlug: "trips",
99 tab: "destinations",
100 label: "Destinations",
101 iconName: "Route",
102 },
103 {
104 key: "trips.categories",
105 parentSlug: "trips",
106 tab: "categories",
107 label: "Categories",
108 iconName: "FolderTree",
109 },
110 {
111 key: "trips.difficulty-levels",
112 parentSlug: "trips",
113 tab: "difficulty-levels",
114 label: "Difficulty Levels",
115 iconName: "TrendingUp",
116 },
117 {
118 key: "trips.availability",
119 parentSlug: "trips",
120 tab: "availability",
121 label: "Availability",
122 iconName: "CalendarDays",
123 },
124 {
125 key: "trips.attributes",
126 parentSlug: "trips",
127 tab: "attributes",
128 label: "Attributes",
129 iconName: "Tag",
130 },
131 {
132 key: "trips.additional-services",
133 parentSlug: "trips",
134 tab: "additional-services",
135 label: "Additional Services",
136 iconName: "Package",
137 },
138 {
139 key: "trips.trip-consent",
140 parentSlug: "trips",
141 tab: "trip-consent",
142 label: "Trip Consent",
143 iconName: "FileSignature",
144 },
145 ],
146 },
147 {
148 slug: "traveler-categories",
149 label: "Traveler Categories",
150 iconName: "Users",
151 },
152 {
153 slug: "itinerary",
154 label: "Itinerary",
155 iconName: "Route",
156 submenu: [
157 {
158 key: "itinerary.item-types",
159 parentSlug: "itinerary",
160 tab: "item-types",
161 label: "Item Types",
162 iconName: "Tag",
163 },
164 {
165 key: "itinerary.items",
166 parentSlug: "itinerary",
167 tab: "items",
168 label: "Items",
169 iconName: "Route",
170 },
171 {
172 key: "itinerary.itinerary",
173 parentSlug: "itinerary",
174 tab: "itinerary",
175 label: "Itinerary",
176 iconName: "FileText",
177 },
178 ],
179 },
180 { slug: "departures", label: "Departures", iconName: "Calendar" },
181 { slug: "discounts", label: "Discounts", iconName: "BadgePercent" },
182 {
183 slug: "payments",
184 label: "Payments",
185 iconName: "CreditCard",
186 submenu: [
187 {
188 key: "payments.all",
189 parentSlug: "payments",
190 tab: "all",
191 label: "All Payments",
192 iconName: "CreditCard",
193 },
194 // Pro "Scheduled Payments" module. Listed so it can be reordered or
195 // hidden in the menu customizer; actual visibility is still gated on the
196 // module flag at render time, like every other Pro-gated entry.
197 {
198 key: "payments.scheduled",
199 parentSlug: "payments",
200 tab: "scheduled",
201 label: "Scheduled",
202 iconName: "CalendarClock",
203 },
204 ],
205 },
206 { slug: "bookings", label: "Bookings", iconName: "Calendar" },
207 { slug: "customers", label: "Customers", iconName: "UserCircle" },
208 { slug: "travelers", label: "Travelers", iconName: "Plane" },
209 { slug: "enquiries", label: "Enquiries", iconName: "MessageSquare" },
210 { slug: "reviews", label: "Reviews", iconName: "Star" },
211 { slug: "reports", label: "Reports", iconName: "BarChart3" },
212 { slug: "email-automation", label: "Email", iconName: "Mail" },
213 {
214 slug: "abandoned-recovery",
215 label: "Abandoned Recovery",
216 iconName: "RotateCcw",
217 },
218 { slug: "dynamic-pricing", label: "Dynamic Pricing", iconName: "TrendingUp" },
219 { slug: "modules", label: "Modules", iconName: "Puzzle" },
220 { slug: "white-label", label: "White Label", iconName: "Crown" },
221 { slug: "ai-assistant", label: "AI Assistant", iconName: "Sparkles" },
222 { slug: "whatsapp", label: "WhatsApp", iconName: "MessageCircle" },
223 { slug: "channel-manager", label: "Channel Manager", iconName: "Network" },
224 { slug: "webhooks", label: "Webhooks", iconName: "Webhook" },
225 { slug: "team", label: "Team & Access", iconName: "Users" },
226 { slug: "license", label: "License", iconName: "Key" },
227 { slug: "settings", label: "Settings", iconName: "Settings" },
228 ];
229
230 export function getDefaultTopLevelSlugs(): string[] {
231 return DEFAULT_MENU_ITEMS.map((item) => item.slug);
232 }
233
234 /**
235 * Read the saved menu order. Supports two stored shapes for backwards
236 * compatibility:
237 * - flat array of slugs → wrapped into { "": [...] } (top-level only)
238 * - object map keyed by parent → returned as-is
239 */
240 export function readMenuOrder(): MenuOrderMap {
241 const raw = (window as any).yatraAdmin?.brandMenuOrder;
242 if (Array.isArray(raw)) {
243 return { "": raw.filter((s): s is string => typeof s === "string") };
244 }
245 if (raw && typeof raw === "object") {
246 const out: MenuOrderMap = {};
247 for (const [key, list] of Object.entries(raw)) {
248 if (typeof key === "string" && Array.isArray(list)) {
249 out[key] = list.filter((s): s is string => typeof s === "string");
250 }
251 }
252 return out;
253 }
254 return {};
255 }
256
257 export interface UiChromeFlags {
258 hideVersion?: boolean;
259 hideBackToWp?: boolean;
260 hideJoinCommunity?: boolean;
261 }
262
263 export function readUiChrome(): UiChromeFlags {
264 const raw = (window as any).yatraAdmin?.brandUiChrome;
265 if (!raw || typeof raw !== "object") return {};
266 return raw as UiChromeFlags;
267 }
268
269 /**
270 * Icons offered in the White Label menu-customization dropdown. Curated
271 * to match icons we already import in Layout.tsx — keeps the runtime
272 * map small and predictable.
273 */
274 export const SELECTABLE_ICON_NAMES = [
275 "LayoutDashboard",
276 "MapPin",
277 "Calendar",
278 "CalendarDays",
279 "Star",
280 "BarChart3",
281 "Settings",
282 "Wrench",
283 "FileText",
284 "CreditCard",
285 "Package",
286 "UserCircle",
287 "Users",
288 "FolderTree",
289 "Tag",
290 "TrendingUp",
291 "List",
292 "Activity",
293 "Crown",
294 "Mail",
295 "Key",
296 "FileSignature",
297 "Route",
298 "BadgePercent",
299 "Plane",
300 "MessageSquare",
301 "Puzzle",
302 "RotateCcw",
303 "Tools",
304 "Sparkles",
305 "MessageCircle",
306 "Network",
307 ];
308
309 export function readMenuOverrides(): MenuOverrides {
310 const raw = (window as any).yatraAdmin?.brandMenuOverrides;
311 if (!raw || typeof raw !== "object") return {};
312 return raw as MenuOverrides;
313 }
314
315 export function effectiveLabel(
316 item: MenuItemDefault,
317 overrides: MenuOverrides,
318 ): string {
319 const override = overrides[item.slug]?.label;
320 return override && override.trim() !== "" ? override : item.label;
321 }
322
323 export function effectiveIconName(
324 item: MenuItemDefault,
325 overrides: MenuOverrides,
326 ): string {
327 // MenuIconValue is `string | IconPickerValue` — only treat the
328 // string variant as a candidate override. IconPickerValue (object
329 // form) is the picker payload, not the legacy lucide-string name
330 // we resolve from a `<MenuIcon name="…" />` mapping table — the
331 // picker form is handled elsewhere via toPickerValue() and isn't
332 // a substitute for the iconName here.
333 const override = overrides[item.slug]?.icon;
334 if (typeof override === "string" && override.trim() !== "") {
335 return override;
336 }
337 return item.iconName;
338 }
339
340 export function isHidden(slug: string, overrides: MenuOverrides): boolean {
341 return Boolean(overrides[slug]?.hidden);
342 }
343