PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.13
Yatra – Travel Booking & Tour Operator Software v3.0.13
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.13, at resources/js/lib/sidebar-menu-defaults.ts

320 lines 9.2 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 { slug: "payments", label: "Payments", iconName: "CreditCard" },
183 { slug: "bookings", label: "Bookings", iconName: "Calendar" },
184 { slug: "customers", label: "Customers", iconName: "UserCircle" },
185 { slug: "travelers", label: "Travelers", iconName: "Plane" },
186 { slug: "enquiries", label: "Enquiries", iconName: "MessageSquare" },
187 { slug: "reviews", label: "Reviews", iconName: "Star" },
188 { slug: "reports", label: "Reports", iconName: "BarChart3" },
189 { slug: "email-automation", label: "Email", iconName: "Mail" },
190 {
191 slug: "abandoned-recovery",
192 label: "Abandoned Recovery",
193 iconName: "RotateCcw",
194 },
195 { slug: "dynamic-pricing", label: "Dynamic Pricing", iconName: "TrendingUp" },
196 { slug: "modules", label: "Modules", iconName: "Puzzle" },
197 { slug: "white-label", label: "White Label", iconName: "Crown" },
198 { slug: "ai-assistant", label: "AI Assistant", iconName: "Sparkles" },
199 { slug: "whatsapp", label: "WhatsApp", iconName: "MessageCircle" },
200 { slug: "channel-manager", label: "Channel Manager", iconName: "Network" },
201 { slug: "webhooks", label: "Webhooks", iconName: "Webhook" },
202 { slug: "team", label: "Team & Access", iconName: "Users" },
203 { slug: "license", label: "License", iconName: "Key" },
204 { slug: "settings", label: "Settings", iconName: "Settings" },
205 ];
206
207 export function getDefaultTopLevelSlugs(): string[] {
208 return DEFAULT_MENU_ITEMS.map((item) => item.slug);
209 }
210
211 /**
212 * Read the saved menu order. Supports two stored shapes for backwards
213 * compatibility:
214 * - flat array of slugs → wrapped into { "": [...] } (top-level only)
215 * - object map keyed by parent → returned as-is
216 */
217 export function readMenuOrder(): MenuOrderMap {
218 const raw = (window as any).yatraAdmin?.brandMenuOrder;
219 if (Array.isArray(raw)) {
220 return { "": raw.filter((s): s is string => typeof s === "string") };
221 }
222 if (raw && typeof raw === "object") {
223 const out: MenuOrderMap = {};
224 for (const [key, list] of Object.entries(raw)) {
225 if (typeof key === "string" && Array.isArray(list)) {
226 out[key] = list.filter((s): s is string => typeof s === "string");
227 }
228 }
229 return out;
230 }
231 return {};
232 }
233
234 export interface UiChromeFlags {
235 hideVersion?: boolean;
236 hideBackToWp?: boolean;
237 hideJoinCommunity?: boolean;
238 }
239
240 export function readUiChrome(): UiChromeFlags {
241 const raw = (window as any).yatraAdmin?.brandUiChrome;
242 if (!raw || typeof raw !== "object") return {};
243 return raw as UiChromeFlags;
244 }
245
246 /**
247 * Icons offered in the White Label menu-customization dropdown. Curated
248 * to match icons we already import in Layout.tsx — keeps the runtime
249 * map small and predictable.
250 */
251 export const SELECTABLE_ICON_NAMES = [
252 "LayoutDashboard",
253 "MapPin",
254 "Calendar",
255 "CalendarDays",
256 "Star",
257 "BarChart3",
258 "Settings",
259 "Wrench",
260 "FileText",
261 "CreditCard",
262 "Package",
263 "UserCircle",
264 "Users",
265 "FolderTree",
266 "Tag",
267 "TrendingUp",
268 "List",
269 "Activity",
270 "Crown",
271 "Mail",
272 "Key",
273 "FileSignature",
274 "Route",
275 "BadgePercent",
276 "Plane",
277 "MessageSquare",
278 "Puzzle",
279 "RotateCcw",
280 "Tools",
281 "Sparkles",
282 "MessageCircle",
283 "Network",
284 ];
285
286 export function readMenuOverrides(): MenuOverrides {
287 const raw = (window as any).yatraAdmin?.brandMenuOverrides;
288 if (!raw || typeof raw !== "object") return {};
289 return raw as MenuOverrides;
290 }
291
292 export function effectiveLabel(
293 item: MenuItemDefault,
294 overrides: MenuOverrides,
295 ): string {
296 const override = overrides[item.slug]?.label;
297 return override && override.trim() !== "" ? override : item.label;
298 }
299
300 export function effectiveIconName(
301 item: MenuItemDefault,
302 overrides: MenuOverrides,
303 ): string {
304 // MenuIconValue is `string | IconPickerValue` — only treat the
305 // string variant as a candidate override. IconPickerValue (object
306 // form) is the picker payload, not the legacy lucide-string name
307 // we resolve from a `<MenuIcon name="…" />` mapping table — the
308 // picker form is handled elsewhere via toPickerValue() and isn't
309 // a substitute for the iconName here.
310 const override = overrides[item.slug]?.icon;
311 if (typeof override === "string" && override.trim() !== "") {
312 return override;
313 }
314 return item.iconName;
315 }
316
317 export function isHidden(slug: string, overrides: MenuOverrides): boolean {
318 return Boolean(overrides[slug]?.hidden);
319 }
320