@fayz-ai/admin 0.61.0 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{CommandPalette-B5PMQFDG.js → CommandPalette-XDJJ5E3O.js} +3 -3
- package/dist/{CommandPalette-B5PMQFDG.js.map → CommandPalette-XDJJ5E3O.js.map} +1 -1
- package/dist/ConnectedBrandingSettings-43ZM3HCI.js +680 -0
- package/dist/ConnectedBrandingSettings-43ZM3HCI.js.map +1 -0
- package/dist/ConnectedCompanySettings-I4MALI2Q.js +197 -0
- package/dist/ConnectedCompanySettings-I4MALI2Q.js.map +1 -0
- package/dist/ConnectedModulesSettings-A2VJ5BMJ.js +563 -0
- package/dist/ConnectedModulesSettings-A2VJ5BMJ.js.map +1 -0
- package/dist/ConnectedSecuritySettings-25AHA4HJ.js +189 -0
- package/dist/ConnectedSecuritySettings-25AHA4HJ.js.map +1 -0
- package/dist/ConnectedUserProfile-E7USJRUE.js +424 -0
- package/dist/ConnectedUserProfile-E7USJRUE.js.map +1 -0
- package/dist/FieldRulesEditor-YGIPKI5T.js +129 -0
- package/dist/FieldRulesEditor-YGIPKI5T.js.map +1 -0
- package/dist/GettingStartedWidget-HMIJ3Y24.js +12 -0
- package/dist/GettingStartedWidget-HMIJ3Y24.js.map +1 -0
- package/dist/PermissionProfilesTab-3ZPRCHMZ.js +548 -0
- package/dist/PermissionProfilesTab-3ZPRCHMZ.js.map +1 -0
- package/dist/PersonTimeline-HQZT527S.js +3 -0
- package/dist/{PersonTimeline-X7JTPUWJ.js.map → PersonTimeline-HQZT527S.js.map} +1 -1
- package/dist/ProjectsSettings-7O2F27MD.js +5 -0
- package/dist/ProjectsSettings-7O2F27MD.js.map +1 -0
- package/dist/ScheduleEditor-54ELWR45.js +8 -0
- package/dist/{ScheduleEditor-L2UJXKMY.js.map → ScheduleEditor-54ELWR45.js.map} +1 -1
- package/dist/SeuSiteWidget-QLSUVC63.js +6 -0
- package/dist/SeuSiteWidget-QLSUVC63.js.map +1 -0
- package/dist/SubscriptionPage-5GE5HFJK.js +17 -0
- package/dist/{SubscriptionPage-IDVEFLZ5.js.map → SubscriptionPage-5GE5HFJK.js.map} +1 -1
- package/dist/TeamTab-6RAULFLW.js +502 -0
- package/dist/TeamTab-6RAULFLW.js.map +1 -0
- package/dist/TrialWidget-Z6CLUCU3.js +7 -0
- package/dist/TrialWidget-Z6CLUCU3.js.map +1 -0
- package/dist/app/AdminShell.d.ts.map +1 -1
- package/dist/app/admin-app.d.ts.map +1 -1
- package/dist/app/config.d.ts +14 -0
- package/dist/app/config.d.ts.map +1 -1
- package/dist/app/routing.d.ts +6 -0
- package/dist/app/routing.d.ts.map +1 -1
- package/dist/app/scaffold.d.ts.map +1 -1
- package/dist/billing/index.js +9 -4
- package/dist/chunk-2H46WDG5.js +53 -0
- package/dist/chunk-2H46WDG5.js.map +1 -0
- package/dist/chunk-3HLX7YB2.js +94 -0
- package/dist/chunk-3HLX7YB2.js.map +1 -0
- package/dist/chunk-3OUD4XBK.js +3 -0
- package/dist/chunk-3OUD4XBK.js.map +1 -0
- package/dist/{chunk-IXSY4GPK.js → chunk-5D5QLMOQ.js} +5 -3
- package/dist/chunk-5D5QLMOQ.js.map +1 -0
- package/dist/chunk-5WR62X2R.js +1081 -0
- package/dist/chunk-5WR62X2R.js.map +1 -0
- package/dist/{chunk-KPYRTOCK.js → chunk-6CQFGMEO.js} +4 -140
- package/dist/chunk-6CQFGMEO.js.map +1 -0
- package/dist/chunk-6MMSYC2I.js +116 -0
- package/dist/chunk-6MMSYC2I.js.map +1 -0
- package/dist/chunk-6QFOE3UD.js +1466 -0
- package/dist/chunk-6QFOE3UD.js.map +1 -0
- package/dist/chunk-6UUFVQ2E.js +283 -0
- package/dist/chunk-6UUFVQ2E.js.map +1 -0
- package/dist/chunk-7WNLL3F6.js +7889 -0
- package/dist/chunk-7WNLL3F6.js.map +1 -0
- package/dist/chunk-A74IHAQE.js +439 -0
- package/dist/chunk-A74IHAQE.js.map +1 -0
- package/dist/{chunk-JOBRLBIQ.js → chunk-AFA6IO23.js} +4 -3
- package/dist/chunk-AFA6IO23.js.map +1 -0
- package/dist/{chunk-P5UIGQFY.js → chunk-ALCSIN3Z.js} +5 -5
- package/dist/chunk-ALCSIN3Z.js.map +1 -0
- package/dist/chunk-AWM4AMBS.js +39 -0
- package/dist/chunk-AWM4AMBS.js.map +1 -0
- package/dist/{chunk-OQARLKZN.js → chunk-BG5Z6KLN.js} +6 -18
- package/dist/chunk-BG5Z6KLN.js.map +1 -0
- package/dist/chunk-DPNFZONC.js +19 -0
- package/dist/chunk-DPNFZONC.js.map +1 -0
- package/dist/chunk-DRCV643L.js +193 -0
- package/dist/chunk-DRCV643L.js.map +1 -0
- package/dist/chunk-E6DF3VR6.js +206 -0
- package/dist/chunk-E6DF3VR6.js.map +1 -0
- package/dist/chunk-ENHCVGXZ.js +120 -0
- package/dist/chunk-ENHCVGXZ.js.map +1 -0
- package/dist/chunk-FK6RHGI7.js +56 -0
- package/dist/chunk-FK6RHGI7.js.map +1 -0
- package/dist/chunk-G5HNA5QK.js +233 -0
- package/dist/chunk-G5HNA5QK.js.map +1 -0
- package/dist/chunk-HCFO4Z6A.js +53 -0
- package/dist/chunk-HCFO4Z6A.js.map +1 -0
- package/dist/chunk-JFIU6NQP.js +51 -0
- package/dist/chunk-JFIU6NQP.js.map +1 -0
- package/dist/chunk-JRZJBMOY.js +3 -0
- package/dist/chunk-JRZJBMOY.js.map +1 -0
- package/dist/{chunk-3YP7KW47.js → chunk-JVVOL4BB.js} +41 -512
- package/dist/chunk-JVVOL4BB.js.map +1 -0
- package/dist/chunk-KAQ3UIAA.js +415 -0
- package/dist/chunk-KAQ3UIAA.js.map +1 -0
- package/dist/{chunk-P6XUDT2P.js → chunk-KJELMULA.js} +4 -51
- package/dist/chunk-KJELMULA.js.map +1 -0
- package/dist/chunk-KLO7DKXX.js +1651 -0
- package/dist/chunk-KLO7DKXX.js.map +1 -0
- package/dist/chunk-M2CMDWUI.js +75 -0
- package/dist/chunk-M2CMDWUI.js.map +1 -0
- package/dist/chunk-PQ47MLTH.js +255 -0
- package/dist/chunk-PQ47MLTH.js.map +1 -0
- package/dist/{chunk-VX3WJLKK.js → chunk-QD7BMUDF.js} +17 -8
- package/dist/chunk-QD7BMUDF.js.map +1 -0
- package/dist/{chunk-E4MESMNR.js → chunk-QTSKCPSW.js} +7 -10
- package/dist/chunk-QTSKCPSW.js.map +1 -0
- package/dist/chunk-S56FQKPL.js +186 -0
- package/dist/chunk-S56FQKPL.js.map +1 -0
- package/dist/chunk-SGAFPNMA.js +75 -0
- package/dist/chunk-SGAFPNMA.js.map +1 -0
- package/dist/{chunk-OD7P4L3D.js → chunk-SOKCLVM4.js} +3 -3
- package/dist/{chunk-OD7P4L3D.js.map → chunk-SOKCLVM4.js.map} +1 -1
- package/dist/chunk-TL42NKTH.js +3 -0
- package/dist/chunk-TL42NKTH.js.map +1 -0
- package/dist/chunk-U6ZCO2PS.js +2964 -0
- package/dist/chunk-U6ZCO2PS.js.map +1 -0
- package/dist/chunk-VSXNROYH.js +16 -0
- package/dist/chunk-VSXNROYH.js.map +1 -0
- package/dist/chunk-WPFZCYZL.js +619 -0
- package/dist/chunk-WPFZCYZL.js.map +1 -0
- package/dist/chunk-XFTNIBDW.js +23 -0
- package/dist/chunk-XFTNIBDW.js.map +1 -0
- package/dist/chunk-Y66JDL6I.js +1020 -0
- package/dist/chunk-Y66JDL6I.js.map +1 -0
- package/dist/chunk-YEHIBPIX.js +137 -0
- package/dist/chunk-YEHIBPIX.js.map +1 -0
- package/dist/chunk-YKPPCV2Z.js +53 -0
- package/dist/chunk-YKPPCV2Z.js.map +1 -0
- package/dist/components/chat/markdown.d.ts.map +1 -1
- package/dist/components/plugins/PluginSettingsPanel.d.ts.map +1 -1
- package/dist/components/settings/SettingsPage.d.ts.map +1 -1
- package/dist/components/shared/ConnectionIndicator.d.ts.map +1 -1
- package/dist/connect-XKQJOWR7.js +18 -0
- package/dist/connect-XKQJOWR7.js.map +1 -0
- package/dist/crud/CrudDetailPage.d.ts.map +1 -1
- package/dist/crud/CrudListView.d.ts.map +1 -1
- package/dist/crud/CrudPage.d.ts.map +1 -1
- package/dist/crud/RelatedListTab.d.ts.map +1 -1
- package/dist/crud/archetypes/EntityLink.d.ts +1 -1
- package/dist/crud/archetypes/EntityLink.d.ts.map +1 -1
- package/dist/crud/record-chrome.d.ts.map +1 -1
- package/dist/entity-routes-PGU7EHAD.js +4 -0
- package/dist/{entity-routes-LXOBW5IB.js.map → entity-routes-PGU7EHAD.js.map} +1 -1
- package/dist/hooks/useModuleNavigation.d.ts.map +1 -1
- package/dist/impersonation/ImpersonateDialog.d.ts.map +1 -1
- package/dist/impersonation/session.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11922 -33564
- package/dist/index.js.map +1 -1
- package/dist/lib/connection.d.ts +14 -0
- package/dist/lib/connection.d.ts.map +1 -1
- package/dist/lib/entity-routes.d.ts.map +1 -1
- package/dist/org/index.js +4 -2
- package/dist/plugins/PluginRegistryManager.d.ts.map +1 -1
- package/dist/settings/MessageTemplateRow.d.ts.map +1 -1
- package/dist/timeline/PersonTimeline.d.ts.map +1 -1
- package/dist/timeline/TimelineList.d.ts.map +1 -1
- package/dist/units/UnitRailPage.d.ts.map +1 -1
- package/dist/units/useUnitFilter.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/PersonTimeline-X7JTPUWJ.js +0 -3
- package/dist/ScheduleEditor-L2UJXKMY.js +0 -6
- package/dist/SubscriptionPage-IDVEFLZ5.js +0 -8
- package/dist/chunk-3YP7KW47.js.map +0 -1
- package/dist/chunk-E4MESMNR.js.map +0 -1
- package/dist/chunk-IXSY4GPK.js.map +0 -1
- package/dist/chunk-JOBRLBIQ.js.map +0 -1
- package/dist/chunk-KPYRTOCK.js.map +0 -1
- package/dist/chunk-OQARLKZN.js.map +0 -1
- package/dist/chunk-P5UIGQFY.js.map +0 -1
- package/dist/chunk-P6XUDT2P.js.map +0 -1
- package/dist/chunk-VX3WJLKK.js.map +0 -1
- package/dist/entity-routes-LXOBW5IB.js +0 -4
|
@@ -0,0 +1,1020 @@
|
|
|
1
|
+
import { SDK_INTEGRATIONS, ConnectorMark } from './chunk-WPFZCYZL.js';
|
|
2
|
+
import { useChatStore } from './chunk-KLO7DKXX.js';
|
|
3
|
+
import { useOrganizationStore } from './chunk-YKPPCV2Z.js';
|
|
4
|
+
import { navigateTo } from './chunk-QD7BMUDF.js';
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { useTourStore, SidebarOnboarding } from '@fayz-ai/ui';
|
|
7
|
+
import { getSupabaseClientOptional, isKnownMissing, rememberIfMissing, tl, useTranslation, countByTenant } from '@fayz-ai/core';
|
|
8
|
+
import { useAuthStore } from '@fayz-ai/auth';
|
|
9
|
+
import { create } from 'zustand';
|
|
10
|
+
import { jsx } from 'react/jsx-runtime';
|
|
11
|
+
import { ONBOARDING_TASK_SOURCE } from '@fayz-ai/tasks/domain/task';
|
|
12
|
+
|
|
13
|
+
var OTHER_CONNECTOR = "other";
|
|
14
|
+
function connectorRows(filter) {
|
|
15
|
+
const { categories, moduleIds, includeSoon = true } = filter;
|
|
16
|
+
const matched = SDK_INTEGRATIONS.filter((row) => {
|
|
17
|
+
if (!includeSoon && row.status !== "available") return false;
|
|
18
|
+
if (categories?.length && !(row.category && categories.includes(row.category))) return false;
|
|
19
|
+
if (moduleIds?.length && !moduleIds.includes(row.moduleId)) return false;
|
|
20
|
+
return true;
|
|
21
|
+
});
|
|
22
|
+
const bySlug = /* @__PURE__ */ new Map();
|
|
23
|
+
for (const row of matched) {
|
|
24
|
+
const seen = bySlug.get(row.slug);
|
|
25
|
+
if (!seen || seen.status !== "available" && row.status === "available") bySlug.set(row.slug, row);
|
|
26
|
+
}
|
|
27
|
+
return [...bySlug.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
28
|
+
}
|
|
29
|
+
function brandMark(slug) {
|
|
30
|
+
const row = SDK_INTEGRATIONS.find((r) => r.slug === slug);
|
|
31
|
+
if (!row) return void 0;
|
|
32
|
+
return /* @__PURE__ */ jsx(ConnectorMark, { connector: { id: row.slug, name: row.name, icon: row.icon, logoUrl: row.logoUrl }, size: "sm" });
|
|
33
|
+
}
|
|
34
|
+
function connectorOptions(filter) {
|
|
35
|
+
const options = connectorRows(filter).map((row) => ({
|
|
36
|
+
value: row.slug,
|
|
37
|
+
label: row.name,
|
|
38
|
+
icon: /* @__PURE__ */ jsx(ConnectorMark, { connector: { id: row.slug, name: row.name, icon: row.icon, logoUrl: row.logoUrl }, size: "sm" })
|
|
39
|
+
}));
|
|
40
|
+
for (const extra of filter.extras ?? []) {
|
|
41
|
+
options.push({ value: extra.value, label: extra.label });
|
|
42
|
+
}
|
|
43
|
+
if (filter.other !== false) {
|
|
44
|
+
options.push({
|
|
45
|
+
value: OTHER_CONNECTOR,
|
|
46
|
+
label: filter.otherLabel ?? tl("Another one", "Outro"),
|
|
47
|
+
hint: tl("Tell us which", "Conta qual")
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return options;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/onboarding/packs/beauty.ts
|
|
54
|
+
var beautyPack = (ctx) => [
|
|
55
|
+
{
|
|
56
|
+
id: "beauty.business",
|
|
57
|
+
eyebrow: tl("Your work", "Seu trabalho"),
|
|
58
|
+
title: tl("What kind of business is it?", "Que tipo de neg\xF3cio \xE9 o seu?"),
|
|
59
|
+
media: ctx.media?.["beauty.business"],
|
|
60
|
+
fields: [
|
|
61
|
+
{
|
|
62
|
+
// The whole funnel for this vertical lives here, so it has to name the
|
|
63
|
+
// businesses that actually buy it — a tattoo studio and a brow bar run
|
|
64
|
+
// the same operation as a salon, and neither recognises itself in
|
|
65
|
+
// "beauty and wellness".
|
|
66
|
+
id: "beauty.businessType",
|
|
67
|
+
kind: "cards",
|
|
68
|
+
required: true,
|
|
69
|
+
columns: 3,
|
|
70
|
+
options: [
|
|
71
|
+
{ value: "salon", label: tl("Hair salon", "Sal\xE3o de beleza") },
|
|
72
|
+
{ value: "barber", label: tl("Barbershop", "Barbearia") },
|
|
73
|
+
{ value: "aesthetics", label: tl("Aesthetics clinic", "Cl\xEDnica de est\xE9tica") },
|
|
74
|
+
{ value: "nails-lashes", label: tl("Nails / lashes studio", "Studio de unhas / c\xEDlios") },
|
|
75
|
+
{ value: "brows-waxing", label: tl("Brows & waxing", "Sobrancelhas / depila\xE7\xE3o") },
|
|
76
|
+
{ value: "tattoo", label: tl("Tattoo / piercing studio", "Studio de tatuagem / piercing") },
|
|
77
|
+
{ value: "spa", label: tl("Spa & massage", "Spa / massagem") },
|
|
78
|
+
{ value: "wellness", label: tl("Wellness practice", "Terapias e bem-estar") },
|
|
79
|
+
{ value: "other", label: tl("Something else", "Outro") }
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: "beauty.businessTypeOther",
|
|
84
|
+
kind: "text",
|
|
85
|
+
label: tl("Which one?", "Qual?"),
|
|
86
|
+
placeholder: tl("The name is enough", "O nome j\xE1 basta"),
|
|
87
|
+
visibleWhen: (v) => v["beauty.businessType"] === "other"
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "beauty.booking",
|
|
93
|
+
eyebrow: tl("Your agenda", "Sua agenda"),
|
|
94
|
+
title: tl("How do clients book with you today?", "Como os clientes marcam hoje?"),
|
|
95
|
+
subtitle: tl(
|
|
96
|
+
"This is the part we replace first, so it is worth being precise.",
|
|
97
|
+
"\xC9 a parte que a gente substitui primeiro, ent\xE3o vale ser preciso."
|
|
98
|
+
),
|
|
99
|
+
media: ctx.media?.["beauty.booking"],
|
|
100
|
+
fields: [
|
|
101
|
+
{
|
|
102
|
+
id: "beauty.bookingToday",
|
|
103
|
+
kind: "cards",
|
|
104
|
+
required: true,
|
|
105
|
+
columns: 2,
|
|
106
|
+
options: [
|
|
107
|
+
{ value: "whatsapp", label: "WhatsApp", hint: tl("One conversation at a time", "Uma conversa de cada vez"), icon: brandMark("whatsapp") },
|
|
108
|
+
{ value: "paper", label: tl("Paper diary", "Agenda de papel") },
|
|
109
|
+
{ value: "google", label: tl("Google Calendar", "Google Agenda"), icon: brandMark("google-calendar") },
|
|
110
|
+
{ value: "system", label: tl("Another system", "Outro sistema"), hint: tl("Tell us which below", "Conta qual abaixo") }
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: "beauty.bookingSystemName",
|
|
115
|
+
kind: "text",
|
|
116
|
+
label: tl("Which system?", "Qual sistema?"),
|
|
117
|
+
placeholder: tl("e.g. Belle, Trinks", "ex.: Belle, Trinks"),
|
|
118
|
+
visibleWhen: (v) => v["beauty.bookingToday"] === "system"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: "beauty.deposit",
|
|
122
|
+
kind: "toggle",
|
|
123
|
+
label: tl("I charge a deposit to hold the slot", "Cobro sinal para segurar o hor\xE1rio")
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: "beauty.reach",
|
|
129
|
+
eyebrow: tl("Where", "Onde"),
|
|
130
|
+
title: tl("Where do you see clients?", "Onde voc\xEA atende?"),
|
|
131
|
+
media: ctx.media?.["beauty.reach"],
|
|
132
|
+
fields: [
|
|
133
|
+
{
|
|
134
|
+
id: "beauty.professionals",
|
|
135
|
+
kind: "chips",
|
|
136
|
+
label: tl("How many professionals work there?", "Quantos profissionais atendem?"),
|
|
137
|
+
required: true,
|
|
138
|
+
options: [
|
|
139
|
+
{ value: "1", label: tl("Just me", "S\xF3 eu") },
|
|
140
|
+
{ value: "2-5", label: "2\u20135" },
|
|
141
|
+
{ value: "6-15", label: "6\u201315" },
|
|
142
|
+
{ value: "gt15", label: tl("More than 15", "Mais de 15") }
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: "beauty.where",
|
|
147
|
+
kind: "cards",
|
|
148
|
+
required: true,
|
|
149
|
+
columns: 3,
|
|
150
|
+
options: [
|
|
151
|
+
{ value: "fixed", label: tl("A fixed space", "Espa\xE7o fixo") },
|
|
152
|
+
{ value: "home-visits", label: tl("At the client", "Na casa do cliente") },
|
|
153
|
+
{ value: "both", label: tl("Both", "Os dois") }
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: "beauty.sellsProducts",
|
|
158
|
+
kind: "toggle",
|
|
159
|
+
label: tl("I also sell products", "Tamb\xE9m vendo produtos")
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
];
|
|
164
|
+
function serves(values, mode) {
|
|
165
|
+
const picked = values["food.serviceModes"];
|
|
166
|
+
return Array.isArray(picked) && picked.includes(mode);
|
|
167
|
+
}
|
|
168
|
+
var foodPack = (ctx) => [
|
|
169
|
+
{
|
|
170
|
+
id: "food.operation",
|
|
171
|
+
eyebrow: tl("Your operation", "Sua opera\xE7\xE3o"),
|
|
172
|
+
title: tl("What kind of place is it?", "Que tipo de opera\xE7\xE3o \xE9 a sua?"),
|
|
173
|
+
media: ctx.media?.["food.operation"],
|
|
174
|
+
fields: [
|
|
175
|
+
{
|
|
176
|
+
id: "food.operation",
|
|
177
|
+
kind: "cards",
|
|
178
|
+
required: true,
|
|
179
|
+
columns: 3,
|
|
180
|
+
options: [
|
|
181
|
+
{ value: "restaurant", label: tl("Restaurant", "Restaurante") },
|
|
182
|
+
{ value: "snack-bar", label: tl("Snack bar", "Lanchonete") },
|
|
183
|
+
{ value: "pizzeria", label: tl("Pizzeria", "Pizzaria") },
|
|
184
|
+
{ value: "cafe", label: tl("Caf\xE9", "Cafeteria") },
|
|
185
|
+
{ value: "bar", label: tl("Bar", "Bar") },
|
|
186
|
+
{ value: "dark-kitchen", label: tl("Dark kitchen", "Dark kitchen") },
|
|
187
|
+
{ value: "food-truck", label: tl("Food truck", "Food truck") },
|
|
188
|
+
{ value: "bakery", label: tl("Bakery", "Padaria") },
|
|
189
|
+
{ value: "other", label: tl("Something else", "Outro") }
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: "food.service",
|
|
196
|
+
eyebrow: tl("Service", "Atendimento"),
|
|
197
|
+
title: tl("How do people order from you?", "Como as pessoas pedem?"),
|
|
198
|
+
subtitle: tl(
|
|
199
|
+
"Each channel turns on a different part of the system \u2014 pick all of them.",
|
|
200
|
+
"Cada canal liga uma parte diferente do sistema \u2014 marque todos."
|
|
201
|
+
),
|
|
202
|
+
media: ctx.media?.["food.service"],
|
|
203
|
+
fields: [
|
|
204
|
+
{
|
|
205
|
+
id: "food.serviceModes",
|
|
206
|
+
kind: "cards",
|
|
207
|
+
multi: true,
|
|
208
|
+
required: true,
|
|
209
|
+
columns: 2,
|
|
210
|
+
options: [
|
|
211
|
+
{ value: "dine-in", label: tl("Dining room", "Sal\xE3o"), hint: tl("Tables and tabs", "Mesas e comandas") },
|
|
212
|
+
{ value: "counter", label: tl("Counter / takeaway", "Balc\xE3o / retirada") },
|
|
213
|
+
{ value: "own-delivery", label: tl("My own delivery", "Delivery pr\xF3prio") },
|
|
214
|
+
{ value: "aggregator", label: tl("Delivery apps", "Apps de entrega"), hint: tl("Someone else takes the order", "O pedido chega por eles") }
|
|
215
|
+
]
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
// The real list, on the same screen as the answer that asks for it.
|
|
219
|
+
// Naming two brands on a card meant guessing which two, and being wrong
|
|
220
|
+
// for everyone on the third.
|
|
221
|
+
id: "food.marketplaceApps",
|
|
222
|
+
kind: "cards",
|
|
223
|
+
multi: true,
|
|
224
|
+
columns: 3,
|
|
225
|
+
label: tl("Which ones?", "Quais?"),
|
|
226
|
+
visibleWhen: (v) => serves(v, "aggregator"),
|
|
227
|
+
options: connectorOptions({
|
|
228
|
+
categories: ["marketplace"],
|
|
229
|
+
moduleIds: ["orders"],
|
|
230
|
+
// No connector of ours yet, but they are half the market — and the
|
|
231
|
+
// answer is about their operation, not our roadmap.
|
|
232
|
+
extras: [
|
|
233
|
+
{ value: "rappi", label: "Rappi" },
|
|
234
|
+
{ value: "aiqfome", label: "aiqfome" },
|
|
235
|
+
{ value: "uber-eats", label: "Uber Eats" }
|
|
236
|
+
]
|
|
237
|
+
})
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
id: "food.marketplaceOther",
|
|
241
|
+
kind: "text",
|
|
242
|
+
label: tl("Which one?", "Qual?"),
|
|
243
|
+
placeholder: tl("The name is enough", "O nome j\xE1 basta"),
|
|
244
|
+
visibleWhen: (v) => Array.isArray(v["food.marketplaceApps"]) && v["food.marketplaceApps"].includes(OTHER_CONNECTOR)
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
id: "food.tables",
|
|
248
|
+
kind: "chips",
|
|
249
|
+
label: tl("How many tables?", "Quantas mesas?"),
|
|
250
|
+
// Counted inside the room, not on the menu page.
|
|
251
|
+
suggestable: false,
|
|
252
|
+
visibleWhen: (v) => serves(v, "dine-in"),
|
|
253
|
+
options: [
|
|
254
|
+
{ value: "lt10", label: tl("Up to 10", "At\xE9 10") },
|
|
255
|
+
{ value: "10-25", label: "10\u201325" },
|
|
256
|
+
{ value: "25-50", label: "25\u201350" },
|
|
257
|
+
{ value: "gt50", label: tl("More than 50", "Mais de 50") }
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
]
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
id: "food.tools",
|
|
264
|
+
eyebrow: tl("Today", "Hoje"),
|
|
265
|
+
title: tl("What are you using right now?", "O que voc\xEA usa hoje?"),
|
|
266
|
+
media: ctx.media?.["food.tools"],
|
|
267
|
+
fields: [
|
|
268
|
+
{
|
|
269
|
+
id: "food.menuToday",
|
|
270
|
+
kind: "cards",
|
|
271
|
+
label: tl("Your menu", "Seu card\xE1pio"),
|
|
272
|
+
columns: 2,
|
|
273
|
+
options: [
|
|
274
|
+
{ value: "none", label: tl("Printed only", "S\xF3 impresso") },
|
|
275
|
+
{ value: "pdf", label: tl("A PDF or photo", "PDF ou foto"), hint: tl("Sent over WhatsApp", "Mandado no WhatsApp") },
|
|
276
|
+
{ value: "aggregator", label: tl("Only in the delivery apps", "S\xF3 nos apps de entrega") },
|
|
277
|
+
{ value: "own-site", label: tl("My own digital menu", "Card\xE1pio digital pr\xF3prio") }
|
|
278
|
+
]
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
id: "food.posToday",
|
|
282
|
+
kind: "cards",
|
|
283
|
+
label: tl("Orders and tabs", "Pedidos e comandas"),
|
|
284
|
+
suggestable: false,
|
|
285
|
+
columns: 2,
|
|
286
|
+
options: [
|
|
287
|
+
{ value: "paper", label: tl("Paper tabs", "Comanda de papel") },
|
|
288
|
+
{ value: "spreadsheet", label: tl("A spreadsheet", "Planilha") },
|
|
289
|
+
{ value: "pos", label: tl("A POS system", "Um PDV"), hint: tl("Tell us which below", "Conta qual abaixo") }
|
|
290
|
+
]
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
id: "food.posName",
|
|
294
|
+
kind: "text",
|
|
295
|
+
label: tl("Which POS?", "Qual PDV?"),
|
|
296
|
+
suggestable: false,
|
|
297
|
+
placeholder: tl("e.g. Consumer, Goomer", "ex.: Consumer, Goomer"),
|
|
298
|
+
visibleWhen: (v) => v["food.posToday"] === "pos"
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
id: "food.volume",
|
|
302
|
+
kind: "chips",
|
|
303
|
+
label: tl("Orders on a normal day", "Pedidos num dia normal"),
|
|
304
|
+
// Nobody publishes their daily volume.
|
|
305
|
+
suggestable: false,
|
|
306
|
+
options: [
|
|
307
|
+
{ value: "lt20", label: tl("Up to 20", "At\xE9 20") },
|
|
308
|
+
{ value: "20-100", label: "20\u2013100" },
|
|
309
|
+
{ value: "100-300", label: "100\u2013300" },
|
|
310
|
+
{ value: "gt300", label: tl("More than 300", "Mais de 300") }
|
|
311
|
+
]
|
|
312
|
+
}
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
];
|
|
316
|
+
var retailPack = (ctx) => [
|
|
317
|
+
{
|
|
318
|
+
id: "retail.catalog",
|
|
319
|
+
eyebrow: tl("Your catalog", "Seu cat\xE1logo"),
|
|
320
|
+
title: tl("What do you sell?", "O que voc\xEA vende?"),
|
|
321
|
+
subtitle: tl(
|
|
322
|
+
"Pick everything that applies \u2014 it decides which categories and fields we set up.",
|
|
323
|
+
"Marque tudo que se aplica \u2014 \xE9 o que define as categorias e os campos que vamos preparar."
|
|
324
|
+
),
|
|
325
|
+
media: ctx.media?.["retail.catalog"],
|
|
326
|
+
fields: [
|
|
327
|
+
{
|
|
328
|
+
id: "retail.segments",
|
|
329
|
+
kind: "chips",
|
|
330
|
+
multi: true,
|
|
331
|
+
required: true,
|
|
332
|
+
options: [
|
|
333
|
+
{ value: "fashion", label: tl("Fashion & accessories", "Moda e acess\xF3rios") },
|
|
334
|
+
{ value: "food", label: tl("Food & drink", "Alimentos e bebidas") },
|
|
335
|
+
{ value: "supplements", label: tl("Supplements", "Suplementos") },
|
|
336
|
+
{ value: "cosmetics", label: tl("Cosmetics", "Cosm\xE9ticos") },
|
|
337
|
+
{ value: "home", label: tl("Home & decor", "Casa e decora\xE7\xE3o") },
|
|
338
|
+
{ value: "electronics", label: tl("Electronics", "Eletr\xF4nicos") },
|
|
339
|
+
{ value: "handmade", label: tl("Handmade", "Artesanal") },
|
|
340
|
+
{ value: "pet", label: tl("Pet", "Pet") },
|
|
341
|
+
{ value: "digital", label: tl("Digital products", "Produtos digitais") },
|
|
342
|
+
{ value: "other", label: tl("Something else", "Outra coisa") }
|
|
343
|
+
]
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
id: "retail.catalogSize",
|
|
347
|
+
kind: "chips",
|
|
348
|
+
label: tl("How many products?", "Quantos produtos?"),
|
|
349
|
+
// A homepage shows a shelf, never the stockroom. Asked to guess, a model
|
|
350
|
+
// reads a full grid as "hundreds" and says so at high confidence.
|
|
351
|
+
suggestable: false,
|
|
352
|
+
options: [
|
|
353
|
+
{ value: "lt20", label: tl("Up to 20", "At\xE9 20") },
|
|
354
|
+
{ value: "20-100", label: "20\u2013100" },
|
|
355
|
+
{ value: "100-1000", label: "100\u20131.000" },
|
|
356
|
+
{ value: "gt1000", label: tl("More than 1,000", "Mais de 1.000") }
|
|
357
|
+
]
|
|
358
|
+
}
|
|
359
|
+
]
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
id: "retail.channels",
|
|
363
|
+
eyebrow: tl("Selling today", "Como vende hoje"),
|
|
364
|
+
title: tl("Where do your sales happen today?", "Por onde suas vendas acontecem hoje?"),
|
|
365
|
+
media: ctx.media?.["retail.channels"],
|
|
366
|
+
fields: [
|
|
367
|
+
{
|
|
368
|
+
id: "retail.channels",
|
|
369
|
+
kind: "cards",
|
|
370
|
+
multi: true,
|
|
371
|
+
required: true,
|
|
372
|
+
columns: 2,
|
|
373
|
+
options: [
|
|
374
|
+
{ value: "store", label: tl("Physical store", "Loja f\xEDsica"), hint: tl("Over the counter", "No balc\xE3o") },
|
|
375
|
+
{ value: "social", label: tl("Instagram / WhatsApp", "Instagram / WhatsApp"), hint: tl("Chat and DMs", "Conversa e direct"), icon: brandMark("whatsapp") },
|
|
376
|
+
{ value: "marketplace", label: tl("Marketplace", "Marketplace"), hint: tl("Someone else owns the storefront", "A vitrine \xE9 de outro") },
|
|
377
|
+
{ value: "own-site", label: tl("My own site", "Site pr\xF3prio"), hint: tl("A store I already run", "Uma loja que j\xE1 tenho") },
|
|
378
|
+
{ value: "none", label: tl("Not selling yet", "Ainda n\xE3o vendo"), hint: tl("Starting from scratch", "Come\xE7ando do zero") }
|
|
379
|
+
]
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
id: "retail.fulfilment",
|
|
383
|
+
kind: "chips",
|
|
384
|
+
multi: true,
|
|
385
|
+
label: tl("And how does it reach the customer?", "E como chega no cliente?"),
|
|
386
|
+
options: [
|
|
387
|
+
{ value: "correios", label: tl("Post office", "Correios") },
|
|
388
|
+
{ value: "melhor-envio", label: "Melhor Envio", icon: brandMark("melhor-envio") },
|
|
389
|
+
{ value: "carrier", label: tl("Carrier", "Transportadora") },
|
|
390
|
+
{ value: "own-delivery", label: tl("My own delivery", "Entrega pr\xF3pria") },
|
|
391
|
+
{ value: "pickup", label: tl("Pickup", "Retirada") },
|
|
392
|
+
{ value: "digital", label: tl("Nothing ships", "N\xE3o envio nada") }
|
|
393
|
+
]
|
|
394
|
+
}
|
|
395
|
+
]
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
id: "retail.payments",
|
|
399
|
+
eyebrow: tl("Getting paid", "Recebimento"),
|
|
400
|
+
title: tl("How do you take payment today?", "Como voc\xEA recebe hoje?"),
|
|
401
|
+
subtitle: tl(
|
|
402
|
+
"We use this to pre-select your payment methods \u2014 nothing is charged here.",
|
|
403
|
+
"Usamos isso para j\xE1 deixar as formas de pagamento certas \u2014 nada \xE9 cobrado aqui."
|
|
404
|
+
),
|
|
405
|
+
media: ctx.media?.["retail.payments"],
|
|
406
|
+
fields: [
|
|
407
|
+
{
|
|
408
|
+
id: "retail.paymentsToday",
|
|
409
|
+
kind: "cards",
|
|
410
|
+
required: true,
|
|
411
|
+
columns: 2,
|
|
412
|
+
options: [
|
|
413
|
+
{ value: "none", label: tl("Not online yet", "Ainda n\xE3o recebo online") },
|
|
414
|
+
{ value: "manual-pix", label: tl("Pix by hand", "Pix na m\xE3o"), hint: tl("Key sent in chat", "Chave enviada na conversa") },
|
|
415
|
+
{ value: "card-machine", label: tl("Card machine", "Maquininha") },
|
|
416
|
+
{ value: "gateway", label: tl("A payment gateway", "Um gateway"), hint: tl("Mercado Pago, Stripe, PagSeguro\u2026", "Mercado Pago, Stripe, PagSeguro\u2026") }
|
|
417
|
+
]
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
// The real catalogue, not a text box: naming the provider they already
|
|
421
|
+
// use is the difference between a note someone reads later and a
|
|
422
|
+
// connector we can offer to set up.
|
|
423
|
+
id: "retail.gateway",
|
|
424
|
+
kind: "cards",
|
|
425
|
+
label: tl("Which one?", "Qual?"),
|
|
426
|
+
columns: 3,
|
|
427
|
+
options: connectorOptions({ categories: ["payments"], moduleIds: ["shop", "financial"] }),
|
|
428
|
+
visibleWhen: (v) => v["retail.paymentsToday"] === "gateway"
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
id: "retail.gatewayOther",
|
|
432
|
+
kind: "text",
|
|
433
|
+
label: tl("Which one?", "Qual?"),
|
|
434
|
+
placeholder: tl("The name is enough", "O nome j\xE1 basta"),
|
|
435
|
+
visibleWhen: (v) => v["retail.gateway"] === OTHER_CONNECTOR
|
|
436
|
+
}
|
|
437
|
+
]
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
id: "retail.marketplaces",
|
|
441
|
+
eyebrow: tl("Channels", "Canais"),
|
|
442
|
+
// A store sells ON a marketplace; a kitchen RECEIVES orders from an app.
|
|
443
|
+
// This step used to borrow the kitchen's wording ("seus pedidos chegam por
|
|
444
|
+
// quais apps?"), which reads as a question about delivery and is the food
|
|
445
|
+
// pack's question — see packs/food.ts, where it belongs.
|
|
446
|
+
title: tl("Where else do you already sell?", "Voc\xEA j\xE1 vende em quais marketplaces?"),
|
|
447
|
+
subtitle: tl(
|
|
448
|
+
"Each one we already connect to is one catalog you stop keeping twice.",
|
|
449
|
+
"Cada um que a gente j\xE1 conecta \xE9 um cat\xE1logo a menos para manter em dobro."
|
|
450
|
+
),
|
|
451
|
+
media: ctx.media?.["retail.marketplaces"],
|
|
452
|
+
// Only worth asking of someone who said they sell on marketplaces.
|
|
453
|
+
visibleWhen: (v) => Array.isArray(v["retail.channels"]) && v["retail.channels"].includes("marketplace"),
|
|
454
|
+
fields: [
|
|
455
|
+
{
|
|
456
|
+
id: "retail.marketplaceApps",
|
|
457
|
+
kind: "cards",
|
|
458
|
+
multi: true,
|
|
459
|
+
columns: 3,
|
|
460
|
+
options: connectorOptions({
|
|
461
|
+
// SCOPED BY MODULE, not just by category. Every `marketplace`
|
|
462
|
+
// connector we ship today is a food-delivery app brought by `orders`
|
|
463
|
+
// (iFood, Rappi, 99Food, Uber Eats…), because that is the only kind
|
|
464
|
+
// built so far — so a category filter alone offered a clothing store
|
|
465
|
+
// iFood and Keeta. `shop` is what makes a sales channel relevant to a
|
|
466
|
+
// store, the same way `orders` is what makes iFood relevant to a
|
|
467
|
+
// kitchen.
|
|
468
|
+
//
|
|
469
|
+
// `ecommerce` is deliberately NOT here either: it is Shopify, which
|
|
470
|
+
// is a storefront somebody runs, not a marketplace they sell on —
|
|
471
|
+
// and `retail.channels` already has "site próprio" for that. Today
|
|
472
|
+
// this matches nothing in the catalogue, so the list is the four
|
|
473
|
+
// extras below; the day a real Mercado Livre connector lands it
|
|
474
|
+
// appears here on its own.
|
|
475
|
+
categories: ["marketplace"],
|
|
476
|
+
moduleIds: ["shop"],
|
|
477
|
+
// Big names with no connector of ours yet. Someone telling us they
|
|
478
|
+
// live on Shopee is telling us about their world, and that answer is
|
|
479
|
+
// worth having whether or not we have built for it.
|
|
480
|
+
extras: [
|
|
481
|
+
{ value: "mercadolivre", label: "Mercado Livre" },
|
|
482
|
+
{ value: "shopee", label: "Shopee" },
|
|
483
|
+
{ value: "amazon", label: "Amazon" },
|
|
484
|
+
{ value: "magalu", label: "Magalu" }
|
|
485
|
+
]
|
|
486
|
+
})
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
id: "retail.marketplaceOther",
|
|
490
|
+
kind: "text",
|
|
491
|
+
label: tl("Which one?", "Qual?"),
|
|
492
|
+
placeholder: tl("The name is enough", "O nome j\xE1 basta"),
|
|
493
|
+
visibleWhen: (v) => Array.isArray(v["retail.marketplaceApps"]) && v["retail.marketplaceApps"].includes(OTHER_CONNECTOR)
|
|
494
|
+
}
|
|
495
|
+
]
|
|
496
|
+
}
|
|
497
|
+
];
|
|
498
|
+
|
|
499
|
+
// src/onboarding/packs/index.ts
|
|
500
|
+
var QUESTION_PACKS = {
|
|
501
|
+
retail: retailPack,
|
|
502
|
+
food: foodPack,
|
|
503
|
+
beauty: beautyPack
|
|
504
|
+
};
|
|
505
|
+
var ALIASES = {
|
|
506
|
+
ecommerce: "retail",
|
|
507
|
+
shop: "retail",
|
|
508
|
+
restaurant: "food",
|
|
509
|
+
resto: "food",
|
|
510
|
+
salon: "beauty"
|
|
511
|
+
};
|
|
512
|
+
function resolveVerticalId(verticalId) {
|
|
513
|
+
if (!verticalId) return void 0;
|
|
514
|
+
const key = verticalId.toLowerCase();
|
|
515
|
+
return ALIASES[key] ?? key;
|
|
516
|
+
}
|
|
517
|
+
function getQuestionPack(verticalId) {
|
|
518
|
+
const resolved = resolveVerticalId(verticalId);
|
|
519
|
+
return resolved && QUESTION_PACKS[resolved] || (() => []);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// src/onboarding/checklist.ts
|
|
523
|
+
var BASE = [
|
|
524
|
+
{
|
|
525
|
+
key: "brand",
|
|
526
|
+
title: "Deixe com a sua cara",
|
|
527
|
+
description: "Logo, cores e o nome que aparece para o cliente.",
|
|
528
|
+
howTo: "Configura\xE7\xF5es \u203A Marca. Suba o logo (PNG ou SVG com fundo transparente), escolha a cor principal e confira o nome que aparece para o cliente. Vale tudo mudar depois.",
|
|
529
|
+
icon: "Palette",
|
|
530
|
+
route: "/settings/branding",
|
|
531
|
+
requiresRoute: true,
|
|
532
|
+
priority: "high",
|
|
533
|
+
celebrate: "Com a sua cara \u{1F3A8}",
|
|
534
|
+
script: [
|
|
535
|
+
{ kind: "say", text: "Primeiro, deixar isto com a sua cara \u2014 logo e cor." },
|
|
536
|
+
{ kind: "goto", route: "/settings/branding" },
|
|
537
|
+
{ kind: "await", text: "Suba o logo e escolha a cor principal. Eu vejo daqui quando ficar pronto." }
|
|
538
|
+
],
|
|
539
|
+
// Reads the workspace we already have in memory — no query, and it closes
|
|
540
|
+
// the moment a logo or a brand colour lands.
|
|
541
|
+
check: async () => {
|
|
542
|
+
const org = useOrganizationStore.getState().currentOrg;
|
|
543
|
+
const branding = org?.settings?.["branding"] ?? {};
|
|
544
|
+
return Boolean(org?.logoUrl || branding["logoUrl"] || branding["primaryColor"]);
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
key: "team",
|
|
549
|
+
title: "Chame quem trabalha com voc\xEA",
|
|
550
|
+
description: "Convide a equipe e diga o que cada um pode ver.",
|
|
551
|
+
howTo: "Configura\xE7\xF5es \u203A Equipe \u203A Convidar. Digite o e-mail da pessoa e escolha o papel dela (o papel \xE9 o que decide o que ela enxerga). Ela recebe um link por e-mail e define a pr\xF3pria senha \u2014 voc\xEA n\xE3o cria senha para ningu\xE9m.",
|
|
552
|
+
icon: "Users",
|
|
553
|
+
route: "/settings/team",
|
|
554
|
+
requiresRoute: true,
|
|
555
|
+
// A pending invite counts: the work was done, and waiting on somebody
|
|
556
|
+
// else's inbox is not a step the owner still owes.
|
|
557
|
+
celebrate: "Convite enviado \u{1F91D}",
|
|
558
|
+
script: [
|
|
559
|
+
{ kind: "say", text: "Vamos chamar quem trabalha com voc\xEA. Te levo at\xE9 a Equipe." },
|
|
560
|
+
{ kind: "goto", route: "/settings/team" },
|
|
561
|
+
{
|
|
562
|
+
kind: "point",
|
|
563
|
+
anchor: "action.invite",
|
|
564
|
+
title: "Convide daqui",
|
|
565
|
+
body: "E-mail da pessoa e o papel dela \u2014 o papel decide o que ela enxerga. Ela define a pr\xF3pria senha pelo link.",
|
|
566
|
+
waitForUser: true
|
|
567
|
+
}
|
|
568
|
+
],
|
|
569
|
+
check: async () => await countByTenant("v_team_members") > 1 || await countByTenant("invitations") > 0,
|
|
570
|
+
tour: {
|
|
571
|
+
id: "workspace.team",
|
|
572
|
+
stops: [
|
|
573
|
+
{
|
|
574
|
+
anchor: "action.invite",
|
|
575
|
+
title: "Convide daqui",
|
|
576
|
+
body: "E-mail da pessoa e o papel dela. Ela define a pr\xF3pria senha pelo link.",
|
|
577
|
+
route: "/settings/team",
|
|
578
|
+
waitForUser: true
|
|
579
|
+
}
|
|
580
|
+
]
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
key: "payments",
|
|
585
|
+
title: "Conecte o recebimento",
|
|
586
|
+
description: "Para o dinheiro cair na conta sem passo manual.",
|
|
587
|
+
howTo: "Configura\xE7\xF5es \u203A Conectores. Escolha o provedor que voc\xEA j\xE1 usa para receber e siga a conex\xE3o dele. Precisa das credenciais da sua conta no provedor \u2014 se n\xE3o tiver em m\xE3os, deixe para quando tiver.",
|
|
588
|
+
icon: "CreditCard",
|
|
589
|
+
route: "/settings/connectors",
|
|
590
|
+
requiresRoute: true,
|
|
591
|
+
priority: "high"
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
key: "publish",
|
|
595
|
+
title: "Publique seu site",
|
|
596
|
+
description: "O endere\xE7o que voc\xEA manda para o cliente.",
|
|
597
|
+
howTo: "Configura\xE7\xF5es \u203A Site. Confira o endere\xE7o, o texto da capa e publique. Depois de publicado, o link j\xE1 pode ir para a bio do Instagram.",
|
|
598
|
+
icon: "Globe",
|
|
599
|
+
route: "/settings/sites",
|
|
600
|
+
requiresRoute: true
|
|
601
|
+
}
|
|
602
|
+
];
|
|
603
|
+
var BY_VERTICAL = {
|
|
604
|
+
retail: {
|
|
605
|
+
key: "catalog",
|
|
606
|
+
title: "Cadastre seu primeiro produto",
|
|
607
|
+
icon: "Package",
|
|
608
|
+
route: "/products",
|
|
609
|
+
requiresRoute: true,
|
|
610
|
+
priority: "high"
|
|
611
|
+
},
|
|
612
|
+
food: {
|
|
613
|
+
key: "catalog",
|
|
614
|
+
title: "Monte seu card\xE1pio",
|
|
615
|
+
icon: "UtensilsCrossed",
|
|
616
|
+
route: "/products",
|
|
617
|
+
requiresRoute: true,
|
|
618
|
+
priority: "high"
|
|
619
|
+
},
|
|
620
|
+
beauty: {
|
|
621
|
+
key: "catalog",
|
|
622
|
+
title: "Cadastre seus servi\xE7os",
|
|
623
|
+
icon: "Sparkles",
|
|
624
|
+
route: "/services",
|
|
625
|
+
requiresRoute: true,
|
|
626
|
+
priority: "high"
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
function onboardingChecklist(verticalId) {
|
|
630
|
+
const resolved = resolveVerticalId(verticalId);
|
|
631
|
+
const vertical = resolved ? BY_VERTICAL[resolved] : void 0;
|
|
632
|
+
return vertical ? [...BASE, vertical] : [...BASE];
|
|
633
|
+
}
|
|
634
|
+
function dueDateFor(seed, from = /* @__PURE__ */ new Date()) {
|
|
635
|
+
if (seed.dueInDays == null) return null;
|
|
636
|
+
const d = new Date(from);
|
|
637
|
+
d.setDate(d.getDate() + seed.dueInDays);
|
|
638
|
+
return d.toISOString().slice(0, 10);
|
|
639
|
+
}
|
|
640
|
+
function mergeOnboardingSteps(catalog, rows, ctx = {}) {
|
|
641
|
+
const byKey = new Map(rows.filter((r) => r.sourceKey).map((r) => [r.sourceKey, r]));
|
|
642
|
+
return catalog.map((seed) => {
|
|
643
|
+
const row = byKey.get(seed.key);
|
|
644
|
+
const closed = row?.status === "done" || row?.status === "cancelled";
|
|
645
|
+
return {
|
|
646
|
+
id: seed.key,
|
|
647
|
+
title: row?.title ?? seed.title,
|
|
648
|
+
description: row?.description ?? seed.description,
|
|
649
|
+
icon: seed.icon,
|
|
650
|
+
action: seed.route,
|
|
651
|
+
// Only `true` is ever asserted. Leaving it undefined for an open row is
|
|
652
|
+
// what lets the sensor still speak; setting it to `false` would silence it.
|
|
653
|
+
done: closed ? true : void 0,
|
|
654
|
+
check: closed || !seed.check ? void 0 : async () => {
|
|
655
|
+
const ok = await seed.check();
|
|
656
|
+
if (ok && row && row.status === "todo") ctx.onComplete?.(seed.key);
|
|
657
|
+
return ok;
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
var TASKS_TABLE = "tasks";
|
|
663
|
+
var TASKS_OBJECT = "tasks";
|
|
664
|
+
async function seedOnboardingTasks(input) {
|
|
665
|
+
const supabase = getSupabaseClientOptional();
|
|
666
|
+
if (!supabase || input.items.length === 0 || isKnownMissing(TASKS_OBJECT)) return false;
|
|
667
|
+
const rows = input.items.map((seed, index) => ({
|
|
668
|
+
tenant_id: input.tenantId,
|
|
669
|
+
title: seed.title,
|
|
670
|
+
// The one-liner AND the instructions: the Tasks panel is where somebody
|
|
671
|
+
// works through this alone at 11pm, and "Conecte o recebimento" with no
|
|
672
|
+
// mechanics under it is a title, not a task.
|
|
673
|
+
description: [seed.description, seed.howTo].filter(Boolean).join("\n\n") || null,
|
|
674
|
+
status: "todo",
|
|
675
|
+
priority: seed.priority ?? "medium",
|
|
676
|
+
due_date: dueDateFor(seed),
|
|
677
|
+
position: index,
|
|
678
|
+
assigned_to_id: input.userId,
|
|
679
|
+
assigned_to_name: input.userName ?? null,
|
|
680
|
+
created_by: input.userId,
|
|
681
|
+
created_by_name: input.userName ?? null,
|
|
682
|
+
source: ONBOARDING_TASK_SOURCE,
|
|
683
|
+
source_key: seed.key
|
|
684
|
+
}));
|
|
685
|
+
try {
|
|
686
|
+
const { error } = await supabase.from(TASKS_TABLE).upsert(rows, { onConflict: "tenant_id,source,source_key", ignoreDuplicates: true });
|
|
687
|
+
if (error) throw error;
|
|
688
|
+
return true;
|
|
689
|
+
} catch (err) {
|
|
690
|
+
rememberIfMissing(TASKS_OBJECT, err);
|
|
691
|
+
console.warn("[onboarding] could not seed the getting-started tasks:", err);
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
function snakeToCamel(obj) {
|
|
696
|
+
const result = {};
|
|
697
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
698
|
+
result[key.replace(/_([a-z])/g, (_, c) => c.toUpperCase())] = value;
|
|
699
|
+
}
|
|
700
|
+
return result;
|
|
701
|
+
}
|
|
702
|
+
async function listOnboardingTasks(tenantId) {
|
|
703
|
+
const supabase = getSupabaseClientOptional();
|
|
704
|
+
if (!supabase || !tenantId || isKnownMissing(TASKS_OBJECT)) return [];
|
|
705
|
+
try {
|
|
706
|
+
const { data, error } = await supabase.from(TASKS_TABLE).select("*").eq("tenant_id", tenantId).eq("source", ONBOARDING_TASK_SOURCE).order("position", { ascending: true });
|
|
707
|
+
if (error) {
|
|
708
|
+
rememberIfMissing(TASKS_OBJECT, error);
|
|
709
|
+
throw error;
|
|
710
|
+
}
|
|
711
|
+
return (data ?? []).map((row) => {
|
|
712
|
+
const t = snakeToCamel(row);
|
|
713
|
+
if (!Array.isArray(t.labels)) t.labels = [];
|
|
714
|
+
return t;
|
|
715
|
+
});
|
|
716
|
+
} catch {
|
|
717
|
+
return [];
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
async function cancelOnboardingTask(tenantId, sourceKey) {
|
|
721
|
+
const supabase = getSupabaseClientOptional();
|
|
722
|
+
if (!supabase || !tenantId) return;
|
|
723
|
+
try {
|
|
724
|
+
await supabase.from(TASKS_TABLE).update({ status: "cancelled" }).eq("tenant_id", tenantId).eq("source", ONBOARDING_TASK_SOURCE).eq("source_key", sourceKey).eq("status", "todo");
|
|
725
|
+
} catch {
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
async function completeOnboardingTask(tenantId, sourceKey) {
|
|
729
|
+
const supabase = getSupabaseClientOptional();
|
|
730
|
+
if (!supabase || !tenantId) return;
|
|
731
|
+
try {
|
|
732
|
+
await supabase.from(TASKS_TABLE).update({ status: "done", completed_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("tenant_id", tenantId).eq("source", ONBOARDING_TASK_SOURCE).eq("source_key", sourceKey).eq("status", "todo");
|
|
733
|
+
} catch {
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// src/onboarding/activation.store.ts
|
|
738
|
+
var useActivationStore = create((set, get) => ({
|
|
739
|
+
rows: null,
|
|
740
|
+
catalog: [],
|
|
741
|
+
loading: false,
|
|
742
|
+
guided: false,
|
|
743
|
+
setCatalog: (catalog) => set({ catalog }),
|
|
744
|
+
setGuided: (guided) => set({ guided }),
|
|
745
|
+
load: async (tenantId) => {
|
|
746
|
+
if (!tenantId) return;
|
|
747
|
+
if (get().tenantId !== tenantId) set({ tenantId, rows: null });
|
|
748
|
+
set({ loading: true });
|
|
749
|
+
const rows = await listOnboardingTasks(tenantId);
|
|
750
|
+
if (get().tenantId !== tenantId) return;
|
|
751
|
+
set({ rows, loading: false });
|
|
752
|
+
},
|
|
753
|
+
complete: async (key, by = "user") => {
|
|
754
|
+
const { tenantId, rows, catalog, onStepCompleted } = get();
|
|
755
|
+
if (!tenantId || !rows) return;
|
|
756
|
+
const row = rows.find((r) => r.sourceKey === key);
|
|
757
|
+
if (!row || row.status === "done" || row.status === "cancelled") return;
|
|
758
|
+
set({
|
|
759
|
+
rows: rows.map(
|
|
760
|
+
(r) => r.sourceKey === key ? { ...r, status: "done", completedAt: (/* @__PURE__ */ new Date()).toISOString() } : r
|
|
761
|
+
)
|
|
762
|
+
});
|
|
763
|
+
await completeOnboardingTask(tenantId, key);
|
|
764
|
+
const step = catalog.find((s) => s.key === key);
|
|
765
|
+
if (step) onStepCompleted?.(step, by);
|
|
766
|
+
}
|
|
767
|
+
}));
|
|
768
|
+
function selectSteps(state) {
|
|
769
|
+
if (!state.rows) return [];
|
|
770
|
+
return mergeOnboardingSteps(state.catalog, state.rows, {
|
|
771
|
+
onComplete: (key) => void useActivationStore.getState().complete(key, "sensor")
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
function openStepKeys(state) {
|
|
775
|
+
const closed = new Set(
|
|
776
|
+
(state.rows ?? []).filter((r) => r.status === "done" || r.status === "cancelled").map((r) => r.sourceKey)
|
|
777
|
+
);
|
|
778
|
+
return new Set(state.catalog.map((s) => s.key).filter((k) => !closed.has(k)));
|
|
779
|
+
}
|
|
780
|
+
function planScript(moves, tourId) {
|
|
781
|
+
const acts = [];
|
|
782
|
+
let stops = [];
|
|
783
|
+
let route;
|
|
784
|
+
const flush = () => {
|
|
785
|
+
if (stops.length === 0) return;
|
|
786
|
+
acts.push({ kind: "tour", tour: { id: tourId, stops } });
|
|
787
|
+
stops = [];
|
|
788
|
+
};
|
|
789
|
+
for (const move of moves) {
|
|
790
|
+
if (move.kind === "point") {
|
|
791
|
+
stops.push({
|
|
792
|
+
anchor: move.anchor,
|
|
793
|
+
title: move.title,
|
|
794
|
+
body: move.body,
|
|
795
|
+
waitForUser: move.waitForUser,
|
|
796
|
+
...stops.length === 0 && route ? { route } : {}
|
|
797
|
+
});
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
flush();
|
|
801
|
+
if (move.kind === "goto") {
|
|
802
|
+
route = move.route;
|
|
803
|
+
acts.push({ kind: "goto", route: move.route });
|
|
804
|
+
} else if (move.kind === "say") {
|
|
805
|
+
acts.push({ kind: "say", text: move.text });
|
|
806
|
+
} else if (move.kind === "await") {
|
|
807
|
+
acts.push({ kind: "await", text: move.text });
|
|
808
|
+
} else if (move.kind === "choose") {
|
|
809
|
+
acts.push({ kind: "choose", id: move.id, question: move.question, options: move.options });
|
|
810
|
+
} else {
|
|
811
|
+
acts.push({ kind: "ask", text: move.text });
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
flush();
|
|
815
|
+
const last = acts[acts.length - 1];
|
|
816
|
+
const waits = last?.kind === "await" || // A question is a wait too: the next move is theirs, and printing over an
|
|
817
|
+
// unanswered question is how a walkthrough starts talking to itself.
|
|
818
|
+
last?.kind === "choose" || last?.kind === "tour" && Boolean(last.tour.stops[last.tour.stops.length - 1]?.waitForUser);
|
|
819
|
+
return { acts, waits };
|
|
820
|
+
}
|
|
821
|
+
var runSeq = 0;
|
|
822
|
+
var current = null;
|
|
823
|
+
function beginRun(key) {
|
|
824
|
+
flushTyping();
|
|
825
|
+
runSeq += 1;
|
|
826
|
+
current = { token: runSeq, key };
|
|
827
|
+
return runSeq;
|
|
828
|
+
}
|
|
829
|
+
function isRunAlive(token) {
|
|
830
|
+
return current?.token === token;
|
|
831
|
+
}
|
|
832
|
+
function currentRunKey() {
|
|
833
|
+
return current?.key ?? null;
|
|
834
|
+
}
|
|
835
|
+
function clearRun() {
|
|
836
|
+
current = null;
|
|
837
|
+
}
|
|
838
|
+
var TYPING_LEAD_MS = 420;
|
|
839
|
+
var TYPING_MS_PER_WORD = 55;
|
|
840
|
+
var TYPING_CEILING_MS = 1200;
|
|
841
|
+
var TYPING_FRAME_MS = 45;
|
|
842
|
+
function say(text, cards) {
|
|
843
|
+
const chat = useChatStore.getState();
|
|
844
|
+
const id = `guided-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
845
|
+
const trimmed = text.trim();
|
|
846
|
+
chat.addMessage({
|
|
847
|
+
id,
|
|
848
|
+
role: "assistant",
|
|
849
|
+
// Empty content is what renders the typing dots (see ChatMessages).
|
|
850
|
+
content: trimmed ? "" : text,
|
|
851
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
852
|
+
...cards ? { cards } : {}
|
|
853
|
+
});
|
|
854
|
+
chat.setOpen(true);
|
|
855
|
+
if (trimmed) typeInto(id, text);
|
|
856
|
+
}
|
|
857
|
+
function typeInto(id, text) {
|
|
858
|
+
const words = text.split(/(\s+)/);
|
|
859
|
+
const wordCount = words.filter((w) => w.trim()).length;
|
|
860
|
+
const total = Math.min(TYPING_CEILING_MS, wordCount * TYPING_MS_PER_WORD);
|
|
861
|
+
const frames = Math.max(1, Math.round(total / TYPING_FRAME_MS));
|
|
862
|
+
const perFrame = Math.max(1, Math.ceil(words.length / frames));
|
|
863
|
+
let cursor = 0;
|
|
864
|
+
let timer;
|
|
865
|
+
const finish = () => {
|
|
866
|
+
if (timer) clearTimeout(timer);
|
|
867
|
+
typers.delete(finish);
|
|
868
|
+
useChatStore.getState().setMessageContent(id, text);
|
|
869
|
+
};
|
|
870
|
+
const step = () => {
|
|
871
|
+
cursor += perFrame;
|
|
872
|
+
if (cursor >= words.length) return finish();
|
|
873
|
+
useChatStore.getState().setMessageContent(id, words.slice(0, cursor).join(""));
|
|
874
|
+
timer = setTimeout(step, TYPING_FRAME_MS);
|
|
875
|
+
};
|
|
876
|
+
typers.add(finish);
|
|
877
|
+
timer = setTimeout(step, TYPING_LEAD_MS);
|
|
878
|
+
}
|
|
879
|
+
var typers = /* @__PURE__ */ new Set();
|
|
880
|
+
function flushTyping() {
|
|
881
|
+
const pending = [...typers];
|
|
882
|
+
typers.clear();
|
|
883
|
+
for (const finish of pending) finish();
|
|
884
|
+
}
|
|
885
|
+
var BEAT_FLOOR_MS = 1400;
|
|
886
|
+
var MS_PER_WORD = 260;
|
|
887
|
+
var BEAT_CEILING_MS = 6e3;
|
|
888
|
+
function readingTime(text) {
|
|
889
|
+
const words = text.trim().split(/\s+/).length;
|
|
890
|
+
return Math.min(BEAT_CEILING_MS, BEAT_FLOOR_MS + words * MS_PER_WORD);
|
|
891
|
+
}
|
|
892
|
+
var NAV_MS = 1e3;
|
|
893
|
+
async function playScript(plan, options = {}) {
|
|
894
|
+
const wait = options.wait ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
895
|
+
const alive = () => options.token === void 0 || isRunAlive(options.token);
|
|
896
|
+
for (const act of plan.acts) {
|
|
897
|
+
if (!alive()) return;
|
|
898
|
+
if (act.kind === "goto") {
|
|
899
|
+
navigateTo(act.route);
|
|
900
|
+
await wait(NAV_MS);
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
if (act.kind === "say") {
|
|
904
|
+
say(act.text);
|
|
905
|
+
await wait(readingTime(act.text));
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
if (act.kind === "choose") {
|
|
909
|
+
say("", [{ kind: "choice", id: act.id, question: act.question, options: act.options }]);
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
if (act.kind === "tour") {
|
|
913
|
+
useTourStore.getState().start(act.tour);
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
if (act.kind === "await") {
|
|
917
|
+
if (act.text) say(act.text);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
useChatStore.getState().queuePrompt(act.text);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
async function runStepScript(step, options) {
|
|
925
|
+
if (!step.script || step.script.length === 0) return false;
|
|
926
|
+
const token = options?.token ?? beginRun(step.key);
|
|
927
|
+
await playScript(planScript(step.script, `guided.${step.key}`), { ...options, token });
|
|
928
|
+
return true;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// src/onboarding/guided.ts
|
|
932
|
+
async function ensureChecklist() {
|
|
933
|
+
const state = useActivationStore.getState();
|
|
934
|
+
if (!state.tenantId || state.catalog.length === 0) return;
|
|
935
|
+
const seeded = new Set((state.rows ?? []).map((r) => r.sourceKey));
|
|
936
|
+
const missing = state.catalog.filter((s) => !seeded.has(s.key));
|
|
937
|
+
if (missing.length === 0) return;
|
|
938
|
+
const user = useAuthStore.getState().user;
|
|
939
|
+
if (!user) return;
|
|
940
|
+
const ok = await seedOnboardingTasks({
|
|
941
|
+
tenantId: state.tenantId,
|
|
942
|
+
userId: user.id,
|
|
943
|
+
userName: user.fullName || user.email,
|
|
944
|
+
items: missing
|
|
945
|
+
});
|
|
946
|
+
if (ok) await useActivationStore.getState().load(state.tenantId);
|
|
947
|
+
}
|
|
948
|
+
async function startGuidedRun(stepKey) {
|
|
949
|
+
useActivationStore.getState().setGuided(true);
|
|
950
|
+
await ensureChecklist();
|
|
951
|
+
const state = useActivationStore.getState();
|
|
952
|
+
const open = openStepKeys(state);
|
|
953
|
+
const step = state.catalog.find((s) => stepKey ? s.key === stepKey : open.has(s.key));
|
|
954
|
+
await runStep(step);
|
|
955
|
+
}
|
|
956
|
+
async function runStep(step) {
|
|
957
|
+
if (!step) {
|
|
958
|
+
useChatStore.getState().queuePrompt(
|
|
959
|
+
tl(
|
|
960
|
+
"Walk me through what is left of the setup, one step at a time.",
|
|
961
|
+
"Me guie pelo que falta da configura\xE7\xE3o, um passo por vez."
|
|
962
|
+
)
|
|
963
|
+
);
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
if (currentRunKey() === step.key) {
|
|
967
|
+
if (step.route) navigateTo(step.route);
|
|
968
|
+
useChatStore.getState().setOpen(true);
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
const token = beginRun(step.key);
|
|
972
|
+
if (await runStepScript(step, { token })) return;
|
|
973
|
+
if (step.route) navigateTo(step.route);
|
|
974
|
+
if (step.tour) useTourStore.getState().start(step.tour);
|
|
975
|
+
useChatStore.getState().queuePrompt(
|
|
976
|
+
tl(
|
|
977
|
+
`Walk me through this setup step: "${step.title}". Take me to the screen, point at what I have to use, and wait while I do it.`,
|
|
978
|
+
`Me guie neste passo da configura\xE7\xE3o: "${step.title}". Me leve at\xE9 a tela, aponte o que eu tenho que usar e espere eu fazer.`
|
|
979
|
+
)
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
function celebrate(step, remaining) {
|
|
983
|
+
const line = step.celebrate ?? tl(`Done: ${step.title}.`, `Feito: ${step.title.toLowerCase()}.`);
|
|
984
|
+
const tail = remaining > 0 ? tl(` ${remaining} left.`, ` Faltam ${remaining}.`) : tl(" That was the last one.", " Esse era o \xFAltimo.");
|
|
985
|
+
say(`${line}${tail}`);
|
|
986
|
+
}
|
|
987
|
+
function GettingStartedWidget() {
|
|
988
|
+
const t = useTranslation();
|
|
989
|
+
const rows = useActivationStore((s) => s.rows);
|
|
990
|
+
const catalog = useActivationStore((s) => s.catalog);
|
|
991
|
+
const steps = React.useMemo(
|
|
992
|
+
() => selectSteps({ ...useActivationStore.getState(), rows, catalog }),
|
|
993
|
+
[rows, catalog]
|
|
994
|
+
);
|
|
995
|
+
const onNavigate = React.useCallback((path) => {
|
|
996
|
+
const step = useActivationStore.getState().catalog.find((s) => s.route === path);
|
|
997
|
+
navigateTo(path);
|
|
998
|
+
if (step?.tour) useTourStore.getState().start(step.tour);
|
|
999
|
+
}, []);
|
|
1000
|
+
const onAskAgent = React.useCallback((step) => {
|
|
1001
|
+
void startGuidedRun(step.id);
|
|
1002
|
+
}, []);
|
|
1003
|
+
if (!rows || rows.length === 0) return null;
|
|
1004
|
+
return /* @__PURE__ */ jsx(
|
|
1005
|
+
SidebarOnboarding,
|
|
1006
|
+
{
|
|
1007
|
+
title: t("tasks.group.onboarding"),
|
|
1008
|
+
steps,
|
|
1009
|
+
onNavigate,
|
|
1010
|
+
onAskAgent,
|
|
1011
|
+
askAgentLabel: tl("Set it up with the assistant", "Configurar com o assistente"),
|
|
1012
|
+
maxVisible: 5,
|
|
1013
|
+
seeAllLabel: tl("+{n} more later", "mais {n} depois")
|
|
1014
|
+
}
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
export { GettingStartedWidget, OTHER_CONNECTOR, QUESTION_PACKS, beautyPack, beginRun, brandMark, cancelOnboardingTask, celebrate, clearRun, connectorOptions, connectorRows, ensureChecklist, foodPack, getQuestionPack, isRunAlive, onboardingChecklist, openStepKeys, resolveVerticalId, retailPack, runStep, say, seedOnboardingTasks, selectSteps, startGuidedRun, useActivationStore };
|
|
1019
|
+
//# sourceMappingURL=chunk-Y66JDL6I.js.map
|
|
1020
|
+
//# sourceMappingURL=chunk-Y66JDL6I.js.map
|