소스 검색

fix: persist integration redirect after login and make phone selection required #97

- Update AuthContext to check for pending_integration_redirect after login
- Store full redirect URL with parameters in sessionStorage before login/signup
- Make phone number selection required with validation
- Disable connect button when no phone number selected
- Show error state when no phone numbers are available
- Add visual indicators for required field

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude 5 달 전
부모
커밋
112ad12711
2개의 변경된 파일53개의 추가작업 그리고 29개의 파일을 삭제
  1. 9 1
      shopcall.ai-main/src/components/context/AuthContext.tsx
  2. 44 28
      shopcall.ai-main/src/pages/IntegrationsRedirect.tsx

+ 9 - 1
shopcall.ai-main/src/components/context/AuthContext.tsx

@@ -170,7 +170,15 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
         if (login_response.success) {
         if (login_response.success) {
             // setIsAuthenticated(true);
             // setIsAuthenticated(true);
             localStorage.setItem("session_data", JSON.stringify(login_response));
             localStorage.setItem("session_data", JSON.stringify(login_response));
-            check_auth("/dashboard");
+
+            // Check for pending integration redirect
+            const pendingRedirect = sessionStorage.getItem('pending_integration_redirect');
+            if (pendingRedirect) {
+                sessionStorage.removeItem('pending_integration_redirect');
+                check_auth(pendingRedirect);
+            } else {
+                check_auth("/dashboard");
+            }
         } else {
         } else {
             throw new Error(login_response.error);
             throw new Error(login_response.error);
         }
         }

+ 44 - 28
shopcall.ai-main/src/pages/IntegrationsRedirect.tsx

@@ -276,7 +276,10 @@ export default function IntegrationsRedirect() {
     if (pendingInstall) {
     if (pendingInstall) {
       sessionStorage.setItem('pending_install', JSON.stringify(pendingInstall));
       sessionStorage.setItem('pending_install', JSON.stringify(pendingInstall));
     }
     }
-    navigate('/login?redirect=/integrations&' + searchParams.toString());
+    // Store the full redirect URL with all parameters for post-login redirect
+    const redirectUrl = `/integrations?${searchParams.toString()}`;
+    sessionStorage.setItem('pending_integration_redirect', redirectUrl);
+    navigate('/login');
   };
   };
 
 
   // Handle signup redirect
   // Handle signup redirect
@@ -285,7 +288,10 @@ export default function IntegrationsRedirect() {
     if (pendingInstall) {
     if (pendingInstall) {
       sessionStorage.setItem('pending_install', JSON.stringify(pendingInstall));
       sessionStorage.setItem('pending_install', JSON.stringify(pendingInstall));
     }
     }
-    navigate('/signup?redirect=/integrations&' + searchParams.toString());
+    // Store the full redirect URL with all parameters for post-signup redirect
+    const redirectUrl = `/integrations?${searchParams.toString()}`;
+    sessionStorage.setItem('pending_integration_redirect', redirectUrl);
+    navigate('/signup');
   };
   };
 
 
   // Handle creating new account for this shop
   // Handle creating new account for this shop
@@ -293,9 +299,12 @@ export default function IntegrationsRedirect() {
     if (pendingInstall) {
     if (pendingInstall) {
       sessionStorage.setItem('pending_install', JSON.stringify(pendingInstall));
       sessionStorage.setItem('pending_install', JSON.stringify(pendingInstall));
     }
     }
+    // Store the full redirect URL with all parameters for post-signup redirect
+    const redirectUrl = `/integrations?${searchParams.toString()}`;
+    sessionStorage.setItem('pending_integration_redirect', redirectUrl);
     // Log out current user and redirect to signup
     // Log out current user and redirect to signup
     localStorage.removeItem('session_data');
     localStorage.removeItem('session_data');
-    navigate('/signup?redirect=/integrations&' + searchParams.toString());
+    navigate('/signup');
   };
   };
 
 
   // Show error state
   // Show error state
@@ -436,6 +445,7 @@ export default function IntegrationsRedirect() {
               <Label className="text-slate-300 flex items-center gap-2">
               <Label className="text-slate-300 flex items-center gap-2">
                 <Phone className="w-4 h-4" />
                 <Phone className="w-4 h-4" />
                 {t('integrations.oauth.selectPhoneNumber', 'Select Phone Number')}
                 {t('integrations.oauth.selectPhoneNumber', 'Select Phone Number')}
+                <span className="text-red-400">*</span>
               </Label>
               </Label>
               {loadingPhoneNumbers ? (
               {loadingPhoneNumbers ? (
                 <div className="flex items-center justify-center py-3">
                 <div className="flex items-center justify-center py-3">
@@ -445,39 +455,45 @@ export default function IntegrationsRedirect() {
                   </span>
                   </span>
                 </div>
                 </div>
               ) : phoneNumbers.length > 0 ? (
               ) : phoneNumbers.length > 0 ? (
-                <Select value={selectedPhoneNumber} onValueChange={setSelectedPhoneNumber}>
-                  <SelectTrigger className="bg-slate-700 border-slate-600 text-white">
-                    <SelectValue placeholder={t('integrations.oauth.selectPhonePlaceholder', 'Select a phone number')} />
-                  </SelectTrigger>
-                  <SelectContent className="bg-slate-700 border-slate-600">
-                    {phoneNumbers.map((phone) => (
-                      <SelectItem key={phone.id} value={phone.id} className="text-white hover:bg-slate-600">
-                        <div className="flex items-center justify-between w-full">
-                          <span>{phone.phone_number}</span>
-                          <span className="ml-2 text-sm text-slate-400">
-                            {phone.monthly_price > 0 ? `${phone.monthly_price} ${phone.currency}/mo` : t('common.free', 'Free')}
-                          </span>
-                        </div>
-                      </SelectItem>
-                    ))}
-                  </SelectContent>
-                </Select>
+                <>
+                  <Select value={selectedPhoneNumber} onValueChange={setSelectedPhoneNumber}>
+                    <SelectTrigger className={`bg-slate-700 border-slate-600 text-white ${!selectedPhoneNumber ? 'border-red-500/50' : ''}`}>
+                      <SelectValue placeholder={t('integrations.oauth.selectPhonePlaceholder', 'Select a phone number')} />
+                    </SelectTrigger>
+                    <SelectContent className="bg-slate-700 border-slate-600">
+                      {phoneNumbers.map((phone) => (
+                        <SelectItem key={phone.id} value={phone.id} className="text-white hover:bg-slate-600">
+                          <div className="flex items-center justify-between w-full">
+                            <span>{phone.phone_number}</span>
+                            <span className="ml-2 text-sm text-slate-400">
+                              {phone.monthly_price > 0 ? `${phone.monthly_price} ${phone.currency}/mo` : t('common.free', 'Free')}
+                            </span>
+                          </div>
+                        </SelectItem>
+                      ))}
+                    </SelectContent>
+                  </Select>
+                  {!selectedPhoneNumber && (
+                    <p className="text-xs text-red-400">
+                      {t('integrations.oauth.phoneNumberRequired', 'Please select a phone number to continue.')}
+                    </p>
+                  )}
+                </>
               ) : (
               ) : (
-                <p className="text-sm text-slate-400 py-2">
-                  {t('integrations.oauth.noPhoneNumbersAvailable', 'No phone numbers available. You can add one later.')}
-                </p>
+                <div className="bg-red-500/10 border border-red-500/30 rounded-md p-3">
+                  <p className="text-sm text-red-400">
+                    {t('integrations.oauth.noPhoneNumbersError', 'No phone numbers available. Please add a phone number first before connecting your store.')}
+                  </p>
+                </div>
               )}
               )}
-              <p className="text-xs text-slate-500">
-                {t('integrations.oauth.phoneNumberOptional', 'Optional - You can also assign a phone number later in settings.')}
-              </p>
             </div>
             </div>
           </div>
           </div>
 
 
           <div className="space-y-3 mt-4">
           <div className="space-y-3 mt-4">
             <Button
             <Button
-              className="w-full bg-cyan-500 hover:bg-cyan-600 text-white"
+              className="w-full bg-cyan-500 hover:bg-cyan-600 text-white disabled:opacity-50 disabled:cursor-not-allowed"
               onClick={completeInstallation}
               onClick={completeInstallation}
-              disabled={completing}
+              disabled={completing || loadingPhoneNumbers || !selectedPhoneNumber || phoneNumbers.length === 0}
             >
             >
               {completing ? (
               {completing ? (
                 <>
                 <>