Skip to content

Commit 4d08e1f

Browse files
committed
fix (onboarding): improved onboarding flow
1 parent 2fa6b29 commit 4d08e1f

File tree

6 files changed

+266
-49
lines changed

6 files changed

+266
-49
lines changed

src/client/app/onboarding/page.js

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,13 @@ const questions = [
155155
id: "location",
156156
question: "Where are you located?",
157157
description:
158-
"This helps with local info like weather. You can type a city or detect automatically.",
158+
"This helps with local info like weather. You can type a city or detect it automatically.",
159159
type: "location",
160160
required: true
161161
},
162162
{
163163
id: "professional-context",
164-
question: "What's your professional world like?",
164+
question: "What is your professional background?",
165165
type: "textarea",
166166
required: true,
167167
placeholder: "e.g., I'm a software developer at a startup..."
@@ -174,12 +174,19 @@ const questions = [
174174
placeholder: "e.g., I enjoy hiking, learning guitar, and soccer.",
175175
icon: <IconHeart />
176176
},
177+
{
178+
id: "needs-pa",
179+
question:
180+
"Do you often juggle multiple priorities, manage a small team, lead projects, and handle countless day-to-day tasks on your own? Many professionals spend too much time scheduling meetings, organizing their calendar, responding to emails, and doing other administrative work that eats into their day. Do you ever wish you had someone to take these repetitive tasks off your plate?\n\nDo you ever feel the need for a personal assistant?",
181+
type: "yes-no",
182+
required: true
183+
},
177184
{
178185
id: "whatsapp_notifications_number",
179186
question:
180-
"If you'd like to enable this, please enter your number with the country code. Otherwise, just press Enter to skip.",
187+
"Please enter your WhatsApp number with the country code.",
181188
type: "text-input",
182-
required: false,
189+
required: true,
183190
placeholder: "+14155552671",
184191
icon: <IconBrandWhatsapp />
185192
}
@@ -191,7 +198,8 @@ const sentientComments = [
191198
"Perfect. Now, to help with local info like weather and places...",
192199
"This helps me understand your professional goals and context.",
193200
"And when you're not working? Tell me about your hobbies.",
194-
"One last thing. I can send you important notifications, task updates, and reminders on WhatsApp. We're in the process of getting an official number, so for now, messages will come from our co-founder Sarthak (+91827507823), who may also occasionally reach out for feedback.",
201+
"Got it. One more thing before we get to the last step...",
202+
"Finally, I will send you important notifications, task updates, and reminders on WhatsApp. We're in the process of getting an official number, so for now, messages will come from our co-founder Sarthak (+91827507823), who may also occasionally reach out for feedback.",
195203
"Awesome! That's all I need. Let's get you set up."
196204
]
197205

@@ -837,6 +845,39 @@ const OnboardingPage = () => {
837845
)}
838846
</div>
839847
)
848+
case "yes-no":
849+
return (
850+
<div className="flex gap-4">
851+
<button
852+
onClick={() => {
853+
handleAnswer(currentQuestion.id, "yes")
854+
setTimeout(handleNext, 100)
855+
}}
856+
className={cn(
857+
"px-6 py-2 rounded-lg font-semibold transition-colors",
858+
answers[currentQuestion.id] === "yes"
859+
? "bg-brand-orange text-brand-black"
860+
: "bg-neutral-700 hover:bg-neutral-600"
861+
)}
862+
>
863+
Yes
864+
</button>
865+
<button
866+
onClick={() => {
867+
handleAnswer(currentQuestion.id, "no")
868+
setTimeout(handleNext, 100)
869+
}}
870+
className={cn(
871+
"px-6 py-2 rounded-lg font-semibold transition-colors",
872+
answers[currentQuestion.id] === "no"
873+
? "bg-brand-orange text-brand-black"
874+
: "bg-neutral-700 hover:bg-neutral-600"
875+
)}
876+
>
877+
No
878+
</button>
879+
</div>
880+
)
840881
default:
841882
return null
842883
}

src/client/app/settings/page.js

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ const WhatsAppSettings = () => {
147147
const [notificationNumber, setNotificationNumber] = useState("")
148148
const [isNotifLoading, setIsNotifLoading] = useState(true)
149149
const [isSaving, setIsSaving] = useState(false)
150+
const [notificationsEnabled, setNotificationsEnabled] = useState(false)
150151

151152
const fetchNotificationSettings = useCallback(async () => {
152153
setIsNotifLoading(true)
@@ -158,6 +159,7 @@ const WhatsAppSettings = () => {
158159
)
159160
const data = await response.json()
160161
setNotificationNumber(data.whatsapp_notifications_number || "")
162+
setNotificationsEnabled(data.notifications_enabled || false)
161163
} catch (error) {
162164
toast.error(error.message)
163165
} finally {
@@ -193,24 +195,29 @@ const WhatsAppSettings = () => {
193195
}
194196
}
195197

196-
const handleRemoveNotifNumber = async () => {
197-
setIsSaving(true)
198+
const handleToggleNotifications = async (enabled) => {
199+
setNotificationsEnabled(enabled)
200+
const toastId = toast.loading(
201+
enabled ? "Enabling notifications..." : "Disabling notifications..."
202+
)
198203
try {
199204
const response = await fetch(
200-
"/api/settings/whatsapp-notifications",
205+
"/api/settings/whatsapp-notifications/toggle",
201206
{
202207
method: "POST",
203208
headers: { "Content-Type": "application/json" },
204-
body: JSON.stringify({ whatsapp_notifications_number: "" })
209+
body: JSON.stringify({ enabled })
205210
}
206211
)
207-
if (!response.ok) throw new Error("Failed to remove number.")
208-
setNotificationNumber("")
209-
toast.success("Notification number removed.")
212+
const data = await response.json()
213+
if (!response.ok) {
214+
setNotificationsEnabled(!enabled)
215+
throw new Error(data.detail || "Failed to update preference.")
216+
}
217+
toast.success(data.message, { id: toastId })
210218
} catch (error) {
219+
setNotificationsEnabled(!enabled)
211220
toast.error(error.message)
212-
} finally {
213-
setIsSaving(false)
214221
}
215222
}
216223

@@ -237,6 +244,40 @@ const WhatsAppSettings = () => {
237244
</div>
238245
) : (
239246
<div className="space-y-4">
247+
<div className="flex items-center justify-between p-3 bg-neutral-800/30 rounded-lg">
248+
<label
249+
htmlFor="whatsapp-toggle"
250+
className="font-medium text-neutral-200"
251+
>
252+
Enable Notifications
253+
</label>
254+
<button
255+
id="whatsapp-toggle"
256+
onClick={() =>
257+
handleToggleNotifications(
258+
!notificationsEnabled
259+
)
260+
}
261+
disabled={!hasNotifNumber}
262+
className={cn(
263+
"relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-brand-orange focus:ring-offset-2 focus:ring-offset-neutral-900",
264+
"disabled:opacity-50 disabled:cursor-not-allowed",
265+
notificationsEnabled
266+
? "bg-green-500"
267+
: "bg-neutral-600"
268+
)}
269+
>
270+
<span
271+
aria-hidden="true"
272+
className={cn(
273+
"pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out",
274+
notificationsEnabled
275+
? "translate-x-5"
276+
: "translate-x-0"
277+
)}
278+
/>
279+
</button>
280+
</div>
240281
<div className="flex flex-col sm:flex-row gap-2">
241282
<div className="relative flex-grow">
242283
<IconBrandWhatsapp
@@ -271,16 +312,6 @@ const WhatsAppSettings = () => {
271312
)}{" "}
272313
{hasNotifNumber ? "Update" : "Save"}
273314
</button>
274-
{hasNotifNumber && (
275-
<button
276-
onClick={handleRemoveNotifNumber}
277-
disabled={isSaving}
278-
className="flex items-center py-2 px-4 rounded-lg bg-red-600/80 hover:bg-red-600 text-white font-medium transition-colors"
279-
>
280-
<IconX className="w-4 h-4 mr-2" />{" "}
281-
Remove
282-
</button>
283-
)}
284315
</div>
285316
</div>
286317
</div>

src/server/main/misc/routes.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
from main.dependencies import mongo_manager, auth_helper, websocket_manager as main_websocket_manager
1616
from pydantic import BaseModel
1717
from workers.tasks import cud_memory_task
18+
from main.settings.google_sheets_utils import update_onboarding_data_in_sheet, update_plan_in_sheet
19+
from main.notifications.whatsapp_client import check_phone_number_exists, send_whatsapp_message
1820

1921
# Google API libraries for validation
2022
from google.oauth2 import service_account
@@ -37,8 +39,12 @@ class UpdatePrivacyFiltersRequest(BaseModel):
3739
@router.post("/onboarding", status_code=status.HTTP_200_OK, summary="Save Onboarding Data")
3840
async def save_onboarding_data_endpoint(
3941
request_body: OnboardingRequest,
40-
user_id: str = Depends(PermissionChecker(required_permissions=["write:profile"]))
42+
payload: dict = Depends(auth_helper.get_decoded_payload_with_claims)
4143
):
44+
user_id = payload.get("sub")
45+
user_email = payload.get("email")
46+
plan = payload.get("plan", "free")
47+
4248
logger.info(f"[{datetime.datetime.now()}] [ONBOARDING] User {user_id}, Data keys: {list(request_body.data.keys())}")
4349
try:
4450
default_privacy_filters = {
@@ -87,6 +93,31 @@ async def save_onboarding_data_endpoint(
8793
if personal_info:
8894
user_data_to_set["personalInfo"] = personal_info
8995

96+
# --- NEW: Process WhatsApp number from onboarding data ---
97+
onboarding_chat_id = None # Variable to hold the chat ID for the feedback message
98+
whatsapp_number = onboarding_data.get("whatsapp_notifications_number", "").strip()
99+
if whatsapp_number:
100+
try:
101+
validation_result = await check_phone_number_exists(whatsapp_number)
102+
if validation_result and validation_result.get("numberExists"):
103+
chat_id = validation_result.get("chatId")
104+
if chat_id:
105+
onboarding_chat_id = chat_id
106+
user_data_to_set["notificationPreferences"] = {
107+
"whatsapp": {
108+
"number": whatsapp_number,
109+
"chatId": chat_id,
110+
"enabled": True # Enable by default
111+
}
112+
}
113+
logger.info(f"WhatsApp number for user {user_id} validated and set for notifications during onboarding.")
114+
else:
115+
logger.warning(f"Could not get chatId for {whatsapp_number} during onboarding for user {user_id}.")
116+
else:
117+
logger.warning(f"WhatsApp number {whatsapp_number} provided by user {user_id} during onboarding is not valid.")
118+
except Exception as e:
119+
logger.error(f"Error validating WhatsApp number during onboarding for user {user_id}: {e}")
120+
90121
# Create the final update payload for MongoDB
91122
# We construct the payload carefully to avoid replacing the entire userData object
92123
update_payload = {}
@@ -98,6 +129,25 @@ async def save_onboarding_data_endpoint(
98129
if not success:
99130
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to save onboarding data.")
100131

132+
# --- Update Google Sheet ---
133+
try:
134+
if user_email:
135+
await update_onboarding_data_in_sheet(user_email, onboarding_data, plan)
136+
else:
137+
logger.warning(f"Could not update Google Sheet for user {user_id} because no email was found in their token.")
138+
except Exception as e:
139+
logger.error(f"Non-critical error: Failed to update Google Sheet during onboarding for user {user_id}. Error: {e}")
140+
141+
# --- Send conditional WhatsApp message ---
142+
if onboarding_data.get("needs-pa") == "yes" and onboarding_chat_id:
143+
try:
144+
feedback_message = "Hi there, I am Sarthak from team Sentient. Thanks for signing up, are you okay with giving feedback and helping us improve the platform to better suit your needs?"
145+
# Call the WhatsApp sender directly, bypassing in-app notifications
146+
await send_whatsapp_message(onboarding_chat_id, feedback_message)
147+
logger.info(f"Sent onboarding feedback request to user {user_id} on WhatsApp.")
148+
except Exception as e:
149+
logger.error(f"Failed to send onboarding feedback WhatsApp message for user {user_id}: {e}")
150+
101151
# --- Dispatch facts to memory ---
102152
try:
103153
fact_templates = {
@@ -163,6 +213,15 @@ async def get_user_data_endpoint(payload: dict = Depends(auth_helper.get_decoded
163213
if not profile_exists or stored_plan != token_plan:
164214
logger.info(f"Updating plan for user {user_id} to '{token_plan}'. Profile exists: {profile_exists}")
165215
await mongo_manager.update_user_profile(user_id, {"userData.plan": token_plan})
216+
217+
# NEW: Update GSheet when plan changes
218+
user_email = payload.get("email")
219+
if user_email and stored_plan != token_plan: # Only update if the plan actually changed
220+
try:
221+
await update_plan_in_sheet(user_email, token_plan)
222+
except Exception as e:
223+
logger.error(f"Failed to update plan in GSheet for {user_email}: {e}")
224+
166225
# Re-fetch the profile after update to ensure we return the latest data
167226
profile_doc = await mongo_manager.get_user_profile(user_id)
168227

0 commit comments

Comments
 (0)