From b688f8e114065c6b26a05571eeb2d8e663d97338 Mon Sep 17 00:00:00 2001 From: chamikaJ Date: Fri, 25 Jul 2025 10:52:07 +0530 Subject: [PATCH] feat(account-setup): enhance account setup process with new survey and task management features - Expanded localization files to include additional text for account setup steps in multiple languages. - Introduced new components for the survey step, allowing users to provide feedback on their needs and preferences. - Implemented task management features, enabling users to add and manage tasks during the account setup process. - Enhanced the organization step with suggestions for organization names based on industry categories. - Improved UI/UX with new design elements and transitions for a smoother user experience. - Updated Redux state management to handle new survey and task data effectively. - Added language switcher functionality to support multilingual users during the setup process. --- .../public/locales/alb/account-setup.json | 96 ++- .../public/locales/de/account-setup.json | 96 ++- .../public/locales/en/account-setup.json | 96 ++- .../public/locales/es/account-setup.json | 96 ++- .../public/locales/pt/account-setup.json | 96 ++- .../public/locales/zh/account-setup.json | 133 +++- .../src/api/survey/survey.api.service.ts | 2 +- .../components/account-setup/members-step.tsx | 355 +++++++---- .../account-setup/organization-step.tsx | 213 ++++++- .../components/account-setup/project-step.tsx | 297 +++++++-- .../components/account-setup/survey-step.tsx | 578 +++++++++++++----- .../components/account-setup/tasks-step.tsx | 212 ++++--- .../account-setup/account-setup.slice.ts | 6 + .../src/pages/account-setup/account-setup.css | 201 ++++++ .../src/pages/account-setup/account-setup.tsx | 101 ++- 15 files changed, 2107 insertions(+), 471 deletions(-) diff --git a/worklenz-frontend/public/locales/alb/account-setup.json b/worklenz-frontend/public/locales/alb/account-setup.json index eac67579..21c50f1d 100644 --- a/worklenz-frontend/public/locales/alb/account-setup.json +++ b/worklenz-frontend/public/locales/alb/account-setup.json @@ -4,30 +4,80 @@ "setupYourAccount": "Konfiguro Llogarinë Tënde në Worklenz.", "organizationStepTitle": "Emërtoni Organizatën Tuaj", "organizationStepLabel": "Zgjidhni një emër për llogarinë tuaj në Worklenz.", + "organizationStepWelcome": "Setup Your Worklenz Account.", + "organizationStepDescription": "Let's start by setting up your organization. This will be the main workspace for your team.", + "organizationStepTooltip": "This name will appear in your workspace and can be changed later in settings.", + "organizationStepNeedIdeas": "Need ideas?", + "organizationStepUseDetected": "Use detected:", + "organizationStepCharacters": "characters", + "organizationStepGoodLength": "Good length", + "organizationStepTooShort": "Too short", + "organizationStepNamingTips": "Naming Tips", + "organizationStepTip1": "Keep it simple and memorable", + "organizationStepTip2": "Reflect your industry or values", + "organizationStepTip3": "Think about future growth", + "organizationStepTip4": "Make it unique and brandable", + "organizationStepSuggestionsTitle": "Name Suggestions", + "organizationStepCategory1": "Tech Companies", + "organizationStepCategory2": "Creative Agencies", + "organizationStepCategory3": "Consulting", + "organizationStepCategory4": "Startups", + "organizationStepSuggestionsNote": "These are just examples to get you started. Choose something that represents your organization.", + "organizationStepPrivacyNote": "Your organization name is private and only visible to your team members.", + "step2Title": "Create your first tasks", + "step2InputLabel": "Type a few tasks that you are going to do in", + "step2AddAnother": "Add another", + "formTitle": "Create your first task.", + "step3Title": "Invite your team to work with", + "orgTypeQuestion": "What best describes your organization?", + "userRoleQuestion": "What's your role?", + "yourNeedsTitle": "What are your main needs?", + "yourNeedsDescription": "Select all that apply to help us set up your workspace", + "yourNeedsQuestion": "How will you primarily use Worklenz?", + "useCaseTaskOrg": "Organize and track tasks", + "useCaseTeamCollab": "Work together seamlessly", + "useCaseResourceMgmt": "Manage time and resources", + "useCaseClientComm": "Stay connected with clients", + "useCaseTimeTrack": "Monitor project hours", + "useCaseOther": "Something else", + "selectedText": "selected", + "previousToolsQuestion": "What tools have you used before? (Optional)", + "previousToolsPlaceholder": "e.g., Asana, Trello, Jira, Monday.com, etc.", + "discoveryTitle": "One last thing...", + "discoveryDescription": "Help us understand how you discovered Worklenz", + "discoveryQuestion": "How did you hear about us?", + "allSetTitle": "You're all set!", + "allSetDescription": "Let's create your first project and get started with Worklenz", + "aboutYouStepName": "About You", + "yourNeedsStepName": "Your Needs", + "discoveryStepName": "Discovery", + "stepProgress": "Step {step} of 3: {title}", + "projectStepHeader": "Let's create your first project", + "projectStepSubheader": "Start from scratch or use a template to get going faster", + "startFromScratch": "Start from scratch", + "templateSelected": "Template selected below", + "quickSuggestions": "Quick suggestions:", + "orText": "OR", + "startWithTemplate": "Start with a template", + "clearToSelectTemplate": "Clear project name above to select a template", + "templateHeadStart": "Get a head start with pre-built project structures", + "browseAllTemplates": "Browse All Templates", + "templatesAvailable": "15+ industry-specific templates available", + "chooseTemplate": "Choose a template that matches your project type", + "createProject": "Create Project", + "templateSoftwareDev": "Software Development", + "templateSoftwareDesc": "Agile sprints, bug tracking, releases", + "templateMarketing": "Marketing Campaign", + "templateMarketingDesc": "Campaign planning, content calendar", + "templateConstruction": "Construction Project", + "templateConstructionDesc": "Phases, permits, contractors", + "templateStartup": "Startup Launch", + "templateStartupDesc": "MVP development, funding, growth", - "projectStepTitle": "Krijoni projektin tuaj të parë", - "projectStepLabel": "Në cilin projekt po punoni aktualisht?", - "projectStepPlaceholder": "p.sh. Plani i Marketingut", - - "tasksStepTitle": "Krijoni detyrat tuaja të para", - "tasksStepLabel": "Shkruani disa detyra që do të kryeni në", - "tasksStepAddAnother": "Shto një tjetër", - - "emailPlaceholder": "Adresa email", - "invalidEmail": "Ju lutemi vendosni një adresë email të vlefshme", - "or": "ose", - "templateButton": "Importo nga shablloni", - "goBack": "Kthehu Mbrapa", - "cancel": "Anulo", - "create": "Krijo", - "templateDrawerTitle": "Zgjidh nga shabllonet", - "step3InputLabel": "Fto me email", - "addAnother": "Shto një tjetër", - "skipForNow": "Kalo tani për tani", - "formTitle": "Krijoni detyrën tuaj të parë.", - "step3Title": "Fto ekipin tënd të punojë me", - "maxMembers": " (Mund të ftoni deri në 5 anëtarë)", - "maxTasks": " (Mund të krijoni deri në 5 detyra)", + "tasksStepTitle": "Shtoni detyrat tuaja të para", + "tasksStepDescription": "Ndani \"{projectName}\" në detyra të zbatueshe për të filluar", + "taskPlaceholder": "Detyra {index} - p.sh., Çfarë duhet bërë?", + "addAnotherTask": "Shtoni një detyrë tjetër ({current}/{max})", "surveyStepTitle": "Na tregoni për ju", "surveyStepLabel": "Na ndihmoni të personalizojmë eksperiencën tuaj në Worklenz duke përgjigjur disa pyetjeve.", diff --git a/worklenz-frontend/public/locales/de/account-setup.json b/worklenz-frontend/public/locales/de/account-setup.json index 1ef9e8d1..ecb37f48 100644 --- a/worklenz-frontend/public/locales/de/account-setup.json +++ b/worklenz-frontend/public/locales/de/account-setup.json @@ -3,7 +3,28 @@ "setupYourAccount": "Richten Sie Ihr Worklenz-Konto ein.", "organizationStepTitle": "Organisation benennen", - "organizationStepLabel": "Wählen Sie einen Namen für Ihr Worklenz-Konto.", + "organizationStepWelcome": "Willkommen bei Worklenz!", + "organizationStepDescription": "Beginnen wir mit der Einrichtung Ihrer Organisation. Dies wird der Hauptarbeitsplatz für Ihr Team.", + "organizationStepLabel": "Organisationsname", + "organizationStepPlaceholder": "z.B. Acme Corporation", + "organizationStepTooltip": "Dieser Name wird in Ihrem Arbeitsbereich angezeigt und kann später in den Einstellungen geändert werden.", + "organizationStepNeedIdeas": "Brauchen Sie Ideen?", + "organizationStepUseDetected": "Erkannt verwenden:", + "organizationStepCharacters": "Zeichen", + "organizationStepGoodLength": "Gute Länge", + "organizationStepTooShort": "Zu kurz", + "organizationStepNamingTips": "Namensgebungstipps", + "organizationStepTip1": "Halten Sie es einfach und einprägsam", + "organizationStepTip2": "Spiegeln Sie Ihre Branche oder Werte wider", + "organizationStepTip3": "Denken Sie an zukünftiges Wachstum", + "organizationStepTip4": "Machen Sie es einzigartig und markenfähig", + "organizationStepSuggestionsTitle": "Namensvorschläge", + "organizationStepCategory1": "Tech-Unternehmen", + "organizationStepCategory2": "Kreativagenturen", + "organizationStepCategory3": "Beratung", + "organizationStepCategory4": "Startups", + "organizationStepSuggestionsNote": "Dies sind nur Beispiele für den Einstieg. Wählen Sie etwas, das Ihre Organisation repräsentiert.", + "organizationStepPrivacyNote": "Ihr Organisationsname ist privat und nur für Ihre Teammitglieder sichtbar.", "projectStepTitle": "Erstellen Sie Ihr erstes Projekt", "projectStepLabel": "An welchem Projekt arbeiten Sie gerade?", @@ -29,6 +50,79 @@ "maxMembers": " (Sie können bis zu 5 Mitglieder einladen)", "maxTasks": " (Sie können bis zu 5 Aufgaben erstellen)", + "membersStepTitle": "Laden Sie Ihr Team ein", + "membersStepDescription": "Teammitglieder zu \"{organizationName}\" hinzufügen und mit der Zusammenarbeit beginnen", + "memberPlaceholder": "Teammitglied {index} - E-Mail-Adresse eingeben", + "validEmailAddress": "Gültige E-Mail-Adresse", + "addAnotherTeamMember": "Weiteres Teammitglied hinzufügen ({current}/{max})", + "canInviteLater": "Sie können Teammitglieder jederzeit später einladen", + "skipStepDescription": "Haben Sie keine E-Mail-Adressen bereit? Kein Problem! Sie können diesen Schritt überspringen und Teammitglieder später über Ihr Projekt-Dashboard einladen.", + + "orgCategoryTech": "Technologieunternehmen", + "orgCategoryCreative": "Kreativagenturen", + "orgCategoryConsulting": "Beratung", + "orgCategoryStartups": "Startups", + "namingTip1": "Halten Sie es einfach und einprägsam", + "namingTip2": "Spiegeln Sie Ihre Branche oder Werte wider", + "namingTip3": "Denken Sie an zukünftiges Wachstum", + "namingTip4": "Machen Sie es einzigartig und markenfähig", + + "aboutYouTitle": "Erzählen Sie uns von sich", + "aboutYouDescription": "Helfen Sie uns, Ihre Erfahrung zu personalisieren", + "orgTypeQuestion": "Was beschreibt Ihre Organisation am besten?", + "userRoleQuestion": "Was ist Ihre Rolle?", + + "yourNeedsTitle": "Was sind Ihre Hauptbedürfnisse?", + "yourNeedsDescription": "Wählen Sie alle zutreffenden aus, um uns bei der Einrichtung Ihres Arbeitsbereichs zu helfen", + "yourNeedsQuestion": "Wie werden Sie Worklenz hauptsächlich nutzen?", + "useCaseTaskOrg": "Aufgaben organisieren und verfolgen", + "useCaseTeamCollab": "Nahtlos zusammenarbeiten", + "useCaseResourceMgmt": "Zeit und Ressourcen verwalten", + "useCaseClientComm": "Mit Kunden in Verbindung bleiben", + "useCaseTimeTrack": "Projektstunden überwachen", + "useCaseOther": "Etwas anderes", + "selectedText": "ausgewählt", + "previousToolsQuestion": "Welche Tools haben Sie zuvor verwendet? (Optional)", + "previousToolsPlaceholder": "z.B. Asana, Trello, Jira, Monday.com, etc.", + + "discoveryTitle": "Eine letzte Sache...", + "discoveryDescription": "Helfen Sie uns zu verstehen, wie Sie Worklenz entdeckt haben", + "discoveryQuestion": "Wie haben Sie von uns erfahren?", + "allSetTitle": "Sie sind bereit!", + "allSetDescription": "Lassen Sie uns Ihr erstes Projekt erstellen und mit Worklenz beginnen", + "aboutYouStepName": "Über Sie", + "yourNeedsStepName": "Ihre Bedürfnisse", + "discoveryStepName": "Entdeckung", + "stepProgress": "Schritt {step} von 3: {title}", + + "projectStepHeader": "Lassen Sie uns Ihr erstes Projekt erstellen", + "projectStepSubheader": "Von Grund auf beginnen oder eine Vorlage verwenden, um schneller voranzukommen", + "startFromScratch": "Von Grund auf beginnen", + "templateSelected": "Vorlage unten ausgewählt", + "quickSuggestions": "Schnelle Vorschläge:", + "orText": "ODER", + "startWithTemplate": "Mit einer Vorlage beginnen", + "clearToSelectTemplate": "Projektname oben löschen, um eine Vorlage auszuwählen", + "templateHeadStart": "Verschaffen Sie sich einen Vorsprung mit vorgefertigten Projektstrukturen", + "browseAllTemplates": "Alle Vorlagen durchsuchen", + "templatesAvailable": "15+ branchenspezifische Vorlagen verfügbar", + "chooseTemplate": "Wählen Sie eine Vorlage, die zu Ihrem Projekttyp passt", + "createProject": "Projekt erstellen", + + "templateSoftwareDev": "Softwareentwicklung", + "templateSoftwareDesc": "Agile Sprints, Fehlerverfolgung, Releases", + "templateMarketing": "Marketing-Kampagne", + "templateMarketingDesc": "Kampagnenplanung, Content-Kalender", + "templateConstruction": "Bauprojekt", + "templateConstructionDesc": "Phasen, Genehmigungen, Auftragnehmer", + "templateStartup": "Startup-Launch", + "templateStartupDesc": "MVP-Entwicklung, Finanzierung, Wachstum", + + "tasksStepTitle": "Fügen Sie Ihre ersten Aufgaben hinzu", + "tasksStepDescription": "Unterteilen Sie \"{projectName}\" in umsetzbare Aufgaben, um zu beginnen", + "taskPlaceholder": "Aufgabe {index} - z.B., Was muss getan werden?", + "addAnotherTask": "Weitere Aufgabe hinzufügen ({current}/{max})", + "surveyStepTitle": "Erzählen Sie uns von sich", "surveyStepLabel": "Helfen Sie uns, Ihre Worklenz-Erfahrung zu personalisieren, indem Sie ein paar Fragen beantworten.", diff --git a/worklenz-frontend/public/locales/en/account-setup.json b/worklenz-frontend/public/locales/en/account-setup.json index e7eb96df..c566d20a 100644 --- a/worklenz-frontend/public/locales/en/account-setup.json +++ b/worklenz-frontend/public/locales/en/account-setup.json @@ -3,7 +3,28 @@ "setupYourAccount": "Setup Your Worklenz Account.", "organizationStepTitle": "Name Your Organization", - "organizationStepLabel": "Pick a name for your Worklenz account.", + "organizationStepWelcome": "Welcome to Worklenz!", + "organizationStepDescription": "Let's start by setting up your organization. This will be the main workspace for your team.", + "organizationStepLabel": "Organization name", + "organizationStepPlaceholder": "e.g. Acme Corporation", + "organizationStepTooltip": "This name will appear in your workspace and can be changed later in settings.", + "organizationStepNeedIdeas": "Need ideas?", + "organizationStepUseDetected": "Use detected:", + "organizationStepCharacters": "characters", + "organizationStepGoodLength": "Good length", + "organizationStepTooShort": "Too short", + "organizationStepNamingTips": "Naming Tips", + "organizationStepTip1": "Keep it simple and memorable", + "organizationStepTip2": "Reflect your industry or values", + "organizationStepTip3": "Think about future growth", + "organizationStepTip4": "Make it unique and brandable", + "organizationStepSuggestionsTitle": "Name Suggestions", + "organizationStepCategory1": "Tech Companies", + "organizationStepCategory2": "Creative Agencies", + "organizationStepCategory3": "Consulting", + "organizationStepCategory4": "Startups", + "organizationStepSuggestionsNote": "These are just examples to get you started. Choose something that represents your organization.", + "organizationStepPrivacyNote": "Your organization name is private and only visible to your team members.", "projectStepTitle": "Create your first project", "projectStepLabel": "What project are you working on right now?", @@ -29,6 +50,79 @@ "maxMembers": " (You can invite up to 5 members)", "maxTasks": " (You can create up to 5 tasks)", + "membersStepTitle": "Invite your team", + "membersStepDescription": "Add team members to \"{organizationName}\" and start collaborating", + "memberPlaceholder": "Team member {index} - Enter email address", + "validEmailAddress": "Valid email address", + "addAnotherTeamMember": "Add another team member ({current}/{max})", + "canInviteLater": "You can always invite team members later", + "skipStepDescription": "Don't have email addresses ready? No problem! You can skip this step and invite team members from your project dashboard later.", + + "orgCategoryTech": "Tech Companies", + "orgCategoryCreative": "Creative Agencies", + "orgCategoryConsulting": "Consulting", + "orgCategoryStartups": "Startups", + "namingTip1": "Keep it simple and memorable", + "namingTip2": "Reflect your industry or values", + "namingTip3": "Think about future growth", + "namingTip4": "Make it unique and brandable", + + "aboutYouTitle": "Tell us about yourself", + "aboutYouDescription": "Help us personalize your experience", + "orgTypeQuestion": "What best describes your organization?", + "userRoleQuestion": "What's your role?", + + "yourNeedsTitle": "What are your main needs?", + "yourNeedsDescription": "Select all that apply to help us set up your workspace", + "yourNeedsQuestion": "How will you primarily use Worklenz?", + "useCaseTaskOrg": "Organize and track tasks", + "useCaseTeamCollab": "Work together seamlessly", + "useCaseResourceMgmt": "Manage time and resources", + "useCaseClientComm": "Stay connected with clients", + "useCaseTimeTrack": "Monitor project hours", + "useCaseOther": "Something else", + "selectedText": "selected", + "previousToolsQuestion": "What tools have you used before? (Optional)", + "previousToolsPlaceholder": "e.g., Asana, Trello, Jira, Monday.com, etc.", + + "discoveryTitle": "One last thing...", + "discoveryDescription": "Help us understand how you discovered Worklenz", + "discoveryQuestion": "How did you hear about us?", + "allSetTitle": "You're all set!", + "allSetDescription": "Let's create your first project and get started with Worklenz", + "aboutYouStepName": "About You", + "yourNeedsStepName": "Your Needs", + "discoveryStepName": "Discovery", + "stepProgress": "Step {step} of 3: {title}", + + "projectStepHeader": "Let's create your first project", + "projectStepSubheader": "Start from scratch or use a template to get going faster", + "startFromScratch": "Start from scratch", + "templateSelected": "Template selected below", + "quickSuggestions": "Quick suggestions:", + "orText": "OR", + "startWithTemplate": "Start with a template", + "clearToSelectTemplate": "Clear project name above to select a template", + "templateHeadStart": "Get a head start with pre-built project structures", + "browseAllTemplates": "Browse All Templates", + "templatesAvailable": "15+ industry-specific templates available", + "chooseTemplate": "Choose a template that matches your project type", + "createProject": "Create Project", + + "templateSoftwareDev": "Software Development", + "templateSoftwareDesc": "Agile sprints, bug tracking, releases", + "templateMarketing": "Marketing Campaign", + "templateMarketingDesc": "Campaign planning, content calendar", + "templateConstruction": "Construction Project", + "templateConstructionDesc": "Phases, permits, contractors", + "templateStartup": "Startup Launch", + "templateStartupDesc": "MVP development, funding, growth", + + "tasksStepTitle": "Add your first tasks", + "tasksStepDescription": "Break down \"{projectName}\" into actionable tasks to get started", + "taskPlaceholder": "Task {index} - e.g., What needs to be done?", + "addAnotherTask": "Add another task ({current}/{max})", + "surveyStepTitle": "Tell us about yourself", "surveyStepLabel": "Help us personalize your Worklenz experience by answering a few questions.", diff --git a/worklenz-frontend/public/locales/es/account-setup.json b/worklenz-frontend/public/locales/es/account-setup.json index 941d7ff6..28092485 100644 --- a/worklenz-frontend/public/locales/es/account-setup.json +++ b/worklenz-frontend/public/locales/es/account-setup.json @@ -3,7 +3,28 @@ "setupYourAccount": "Configura tu cuenta.", "organizationStepTitle": "Nombra tu organización", - "organizationStepLabel": "Elige un nombre para tu cuenta de Worklenz.", + "organizationStepWelcome": "¡Bienvenido a Worklenz!", + "organizationStepDescription": "Comencemos configurando tu organización. Este será el espacio de trabajo principal para tu equipo.", + "organizationStepLabel": "Nombre de la organización", + "organizationStepPlaceholder": "ej. Corporación Acme", + "organizationStepTooltip": "Este nombre aparecerá en tu espacio de trabajo y se puede cambiar más tarde en la configuración.", + "organizationStepNeedIdeas": "¿Necesitas ideas?", + "organizationStepUseDetected": "Usar detectado:", + "organizationStepCharacters": "caracteres", + "organizationStepGoodLength": "Buena longitud", + "organizationStepTooShort": "Demasiado corto", + "organizationStepNamingTips": "Consejos para nombrar", + "organizationStepTip1": "Manténlo simple y memorable", + "organizationStepTip2": "Refleja tu industria o valores", + "organizationStepTip3": "Piensa en el crecimiento futuro", + "organizationStepTip4": "Hazlo único y reconocible", + "organizationStepSuggestionsTitle": "Sugerencias de nombres", + "organizationStepCategory1": "Empresas tecnológicas", + "organizationStepCategory2": "Agencias creativas", + "organizationStepCategory3": "Consultoría", + "organizationStepCategory4": "Startups", + "organizationStepSuggestionsNote": "Estos son solo ejemplos para empezar. Elige algo que represente a tu organización.", + "organizationStepPrivacyNote": "El nombre de tu organización es privado y solo visible para los miembros de tu equipo.", "projectStepTitle": "Crea tu primer proyecto", "projectStepLabel": "¿En qué proyecto estás trabajando ahora?", @@ -30,6 +51,79 @@ "maxMembers": " (Puedes invitar hasta 5 miembros)", "maxTasks": " (Puedes crear hasta 5 tareas)", + "membersStepTitle": "Invita a tu equipo", + "membersStepDescription": "Añade miembros del equipo a \"{organizationName}\" y comienza a colaborar", + "memberPlaceholder": "Miembro del equipo {index} - Ingresa dirección de correo", + "validEmailAddress": "Dirección de correo válida", + "addAnotherTeamMember": "Añadir otro miembro del equipo ({current}/{max})", + "canInviteLater": "Siempre puedes invitar miembros del equipo más tarde", + "skipStepDescription": "¿No tienes direcciones de correo listas? ¡No hay problema! Puedes omitir este paso e invitar miembros del equipo desde tu panel de proyecto más tarde.", + + "orgCategoryTech": "Empresas Tecnológicas", + "orgCategoryCreative": "Agencias Creativas", + "orgCategoryConsulting": "Consultoría", + "orgCategoryStartups": "Startups", + "namingTip1": "Manténlo simple y memorable", + "namingTip2": "Refleja tu industria o valores", + "namingTip3": "Piensa en el crecimiento futuro", + "namingTip4": "Hazlo único y reconocible", + + "aboutYouTitle": "Cuéntanos sobre ti", + "aboutYouDescription": "Ayúdanos a personalizar tu experiencia", + "orgTypeQuestion": "¿Qué describe mejor tu organización?", + "userRoleQuestion": "¿Cuál es tu rol?", + + "yourNeedsTitle": "¿Cuáles son tus principales necesidades?", + "yourNeedsDescription": "Selecciona todas las que apliquen para ayudarnos a configurar tu espacio de trabajo", + "yourNeedsQuestion": "¿Cómo usarás principalmente Worklenz?", + "useCaseTaskOrg": "Organizar y hacer seguimiento de tareas", + "useCaseTeamCollab": "Trabajar juntos sin problemas", + "useCaseResourceMgmt": "Gestionar tiempo y recursos", + "useCaseClientComm": "Mantenerse conectado con clientes", + "useCaseTimeTrack": "Monitorear horas de proyecto", + "useCaseOther": "Algo más", + "selectedText": "seleccionado", + "previousToolsQuestion": "¿Qué herramientas has usado antes? (Opcional)", + "previousToolsPlaceholder": "ej., Asana, Trello, Jira, Monday.com, etc.", + + "discoveryTitle": "Una última cosa...", + "discoveryDescription": "Ayúdanos a entender cómo descubriste Worklenz", + "discoveryQuestion": "¿Cómo te enteraste de nosotros?", + "allSetTitle": "¡Ya estás listo!", + "allSetDescription": "Vamos a crear tu primer proyecto y comenzar con Worklenz", + "aboutYouStepName": "Sobre ti", + "yourNeedsStepName": "Tus necesidades", + "discoveryStepName": "Descubrimiento", + "stepProgress": "Paso {step} de 3: {title}", + + "projectStepHeader": "Vamos a crear tu primer proyecto", + "projectStepSubheader": "Empieza desde cero o usa una plantilla para ir más rápido", + "startFromScratch": "Empezar desde cero", + "templateSelected": "Plantilla seleccionada abajo", + "quickSuggestions": "Sugerencias rápidas:", + "orText": "O", + "startWithTemplate": "Comenzar con una plantilla", + "clearToSelectTemplate": "Borra el nombre del proyecto arriba para seleccionar una plantilla", + "templateHeadStart": "Obtén una ventaja inicial con estructuras de proyecto pre-construidas", + "browseAllTemplates": "Explorar todas las plantillas", + "templatesAvailable": "15+ plantillas específicas de industria disponibles", + "chooseTemplate": "Elige una plantilla que coincida con tu tipo de proyecto", + "createProject": "Crear proyecto", + + "templateSoftwareDev": "Desarrollo de Software", + "templateSoftwareDesc": "Sprints ágiles, seguimiento de errores, lanzamientos", + "templateMarketing": "Campaña de Marketing", + "templateMarketingDesc": "Planificación de campaña, calendario de contenido", + "templateConstruction": "Proyecto de Construcción", + "templateConstructionDesc": "Fases, permisos, contratistas", + "templateStartup": "Lanzamiento de Startup", + "templateStartupDesc": "Desarrollo MVP, financiación, crecimiento", + + "tasksStepTitle": "Añade tus primeras tareas", + "tasksStepDescription": "Desglosa \"{projectName}\" en tareas accionables para comenzar", + "taskPlaceholder": "Tarea {index} - ej., ¿Qué necesita hacerse?", + "addAnotherTask": "Añadir otra tarea ({current}/{max})", + "surveyStepTitle": "Cuéntanos sobre ti", "surveyStepLabel": "Ayúdanos a personalizar tu experiencia de Worklenz respondiendo algunas preguntas.", diff --git a/worklenz-frontend/public/locales/pt/account-setup.json b/worklenz-frontend/public/locales/pt/account-setup.json index 2b11772d..abbd6500 100644 --- a/worklenz-frontend/public/locales/pt/account-setup.json +++ b/worklenz-frontend/public/locales/pt/account-setup.json @@ -3,7 +3,28 @@ "setupYourAccount": "Configure sua conta.", "organizationStepTitle": "Nomeie sua organização", - "organizationStepLabel": "Escolha um nome para sua conta Worklenz.", + "organizationStepWelcome": "Bem-vindo ao Worklenz!", + "organizationStepDescription": "Vamos começar configurando sua organização. Este será o espaço de trabalho principal para sua equipe.", + "organizationStepLabel": "Nome da organização", + "organizationStepPlaceholder": "ex. Corporação Acme", + "organizationStepTooltip": "Este nome aparecerá em seu espaço de trabalho e pode ser alterado posteriormente nas configurações.", + "organizationStepNeedIdeas": "Precisa de ideias?", + "organizationStepUseDetected": "Usar detectado:", + "organizationStepCharacters": "caracteres", + "organizationStepGoodLength": "Bom comprimento", + "organizationStepTooShort": "Muito curto", + "organizationStepNamingTips": "Dicas de nomenclatura", + "organizationStepTip1": "Mantenha simples e memorável", + "organizationStepTip2": "Reflita sua indústria ou valores", + "organizationStepTip3": "Pense no crescimento futuro", + "organizationStepTip4": "Torne único e marcante", + "organizationStepSuggestionsTitle": "Sugestões de nomes", + "organizationStepCategory1": "Empresas de tecnologia", + "organizationStepCategory2": "Agências criativas", + "organizationStepCategory3": "Consultoria", + "organizationStepCategory4": "Startups", + "organizationStepSuggestionsNote": "Estes são apenas exemplos para começar. Escolha algo que represente sua organização.", + "organizationStepPrivacyNote": "O nome da sua organização é privado e visível apenas para os membros da sua equipe.", "projectStepTitle": "Crie seu primeiro projeto", "projectStepLabel": "Em qual projeto você está trabalhando agora?", @@ -30,6 +51,79 @@ "maxMembers": " (Você pode convidar até 5 membros)", "maxTasks": " (Você pode criar até 5 tarefas)", + "membersStepTitle": "Convide sua equipe", + "membersStepDescription": "Adicione membros da equipe ao \"{organizationName}\" e comece a colaborar", + "memberPlaceholder": "Membro da equipe {index} - Digite o endereço de email", + "validEmailAddress": "Endereço de email válido", + "addAnotherTeamMember": "Adicionar outro membro da equipe ({current}/{max})", + "canInviteLater": "Você sempre pode convidar membros da equipe mais tarde", + "skipStepDescription": "Não tem endereços de email prontos? Sem problema! Você pode pular esta etapa e convidar membros da equipe do seu painel de projeto mais tarde.", + + "orgCategoryTech": "Empresas de Tecnologia", + "orgCategoryCreative": "Agências Criativas", + "orgCategoryConsulting": "Consultoria", + "orgCategoryStartups": "Startups", + "namingTip1": "Mantenha simples e memorável", + "namingTip2": "Reflita sua indústria ou valores", + "namingTip3": "Pense no crescimento futuro", + "namingTip4": "Torne único e marcante", + + "aboutYouTitle": "Conte-nos sobre você", + "aboutYouDescription": "Ajude-nos a personalizar sua experiência", + "orgTypeQuestion": "O que melhor descreve sua organização?", + "userRoleQuestion": "Qual é seu papel?", + + "yourNeedsTitle": "Quais são suas principais necessidades?", + "yourNeedsDescription": "Selecione todas que se aplicam para nos ajudar a configurar seu espaço de trabalho", + "yourNeedsQuestion": "Como você usará principalmente o Worklenz?", + "useCaseTaskOrg": "Organizar e acompanhar tarefas", + "useCaseTeamCollab": "Trabalhar juntos perfeitamente", + "useCaseResourceMgmt": "Gerenciar tempo e recursos", + "useCaseClientComm": "Manter-se conectado com clientes", + "useCaseTimeTrack": "Monitorar horas do projeto", + "useCaseOther": "Algo mais", + "selectedText": "selecionado", + "previousToolsQuestion": "Que ferramentas você usou antes? (Opcional)", + "previousToolsPlaceholder": "ex., Asana, Trello, Jira, Monday.com, etc.", + + "discoveryTitle": "Uma última coisa...", + "discoveryDescription": "Ajude-nos a entender como você descobriu o Worklenz", + "discoveryQuestion": "Como você soube sobre nós?", + "allSetTitle": "Você está pronto!", + "allSetDescription": "Vamos criar seu primeiro projeto e começar com o Worklenz", + "aboutYouStepName": "Sobre você", + "yourNeedsStepName": "Suas necessidades", + "discoveryStepName": "Descoberta", + "stepProgress": "Passo {step} de 3: {title}", + + "projectStepHeader": "Vamos criar seu primeiro projeto", + "projectStepSubheader": "Comece do zero ou use um modelo para ir mais rápido", + "startFromScratch": "Começar do zero", + "templateSelected": "Modelo selecionado abaixo", + "quickSuggestions": "Sugestões rápidas:", + "orText": "OU", + "startWithTemplate": "Começar com um modelo", + "clearToSelectTemplate": "Limpe o nome do projeto acima para selecionar um modelo", + "templateHeadStart": "Obtenha uma vantagem inicial com estruturas de projeto pré-construídas", + "browseAllTemplates": "Navegar por todos os modelos", + "templatesAvailable": "15+ modelos específicos da indústria disponíveis", + "chooseTemplate": "Escolha um modelo que corresponda ao seu tipo de projeto", + "createProject": "Criar projeto", + + "templateSoftwareDev": "Desenvolvimento de Software", + "templateSoftwareDesc": "Sprints ágeis, rastreamento de bugs, lançamentos", + "templateMarketing": "Campanha de Marketing", + "templateMarketingDesc": "Planejamento de campanha, calendário de conteúdo", + "templateConstruction": "Projeto de Construção", + "templateConstructionDesc": "Fases, licenças, empreiteiros", + "templateStartup": "Lançamento de Startup", + "templateStartupDesc": "Desenvolvimento MVP, financiamento, crescimento", + + "tasksStepTitle": "Adicione suas primeiras tarefas", + "tasksStepDescription": "Divida \"{projectName}\" em tarefas acionáveis para começar", + "taskPlaceholder": "Tarefa {index} - ex., O que precisa ser feito?", + "addAnotherTask": "Adicionar outra tarefa ({current}/{max})", + "surveyStepTitle": "Conte-nos sobre você", "surveyStepLabel": "Ajude-nos a personalizar sua experiência no Worklenz respondendo algumas perguntas.", diff --git a/worklenz-frontend/public/locales/zh/account-setup.json b/worklenz-frontend/public/locales/zh/account-setup.json index 40e3ac15..86599fd2 100644 --- a/worklenz-frontend/public/locales/zh/account-setup.json +++ b/worklenz-frontend/public/locales/zh/account-setup.json @@ -1,14 +1,38 @@ { "continue": "继续", - "setupYourAccount": "设置您的Worklenz账户。", + "setupYourAccount": "设置您的 Worklenz 账户。", "organizationStepTitle": "命名您的组织", - "organizationStepLabel": "为您的Worklenz账户选择一个名称。", + "organizationStepWelcome": "欢迎使用 Worklenz!", + "organizationStepDescription": "让我们从设置您的组织开始。这将是您团队的主要工作空间。", + "organizationStepLabel": "组织名称", + "organizationStepPlaceholder": "例如:Acme 公司", + "organizationStepTooltip": "此名称将显示在您的工作区,并可在设置中更改。", + "organizationStepNeedIdeas": "需要灵感?", + "organizationStepUseDetected": "检测到使用:", + "organizationStepCharacters": "字符", + "organizationStepGoodLength": "长度合适", + "organizationStepTooShort": "太短", + "organizationStepNamingTips": "命名建议", + "organizationStepTip1": "保持简单且易记", + "organizationStepTip2": "体现您的行业或价值观", + "organizationStepTip3": "考虑未来发展", + "organizationStepTip4": "使其独特且有品牌感", + "organizationStepSuggestionsTitle": "名称建议", + "organizationStepCategory1": "科技公司", + "organizationStepCategory2": "创意机构", + "organizationStepCategory3": "咨询公司", + "organizationStepCategory4": "初创企业", + "organizationStepSuggestionsNote": "这些只是帮助您入门的示例。请选择能代表您组织的名称。", + "organizationStepPrivacyNote": "您的组织名称是私有的,仅团队成员可见。", + "projectStepTitle": "创建您的第一个项目", "projectStepLabel": "您现在正在做什么项目?", "projectStepPlaceholder": "例如:营销计划", + "tasksStepTitle": "创建您的第一个任务", "tasksStepLabel": "输入您将在其中完成的几个任务", "tasksStepAddAnother": "添加另一个", + "emailPlaceholder": "电子邮件地址", "invalidEmail": "请输入有效的电子邮件地址", "or": "或", @@ -22,44 +46,117 @@ "skipForNow": "暂时跳过", "formTitle": "创建您的第一个任务。", "step3Title": "邀请您的团队一起工作", - "maxMembers": "(您最多可以邀请5名成员)", - "maxTasks": "(您最多可以创建5个任务)", + "maxMembers": "(您最多可以邀请 5 名成员)", + "maxTasks": "(您最多可以创建 5 个任务)", + + "membersStepTitle": "邀请您的团队", + "membersStepDescription": "将团队成员添加到 \"{organizationName}\" 并开始协作", + "memberPlaceholder": "团队成员 {index} - 输入电子邮件地址", + "validEmailAddress": "有效的电子邮件地址", + "addAnotherTeamMember": "添加另一个团队成员 ({current}/{max})", + "canInviteLater": "您可以稍后邀请团队成员", + "skipStepDescription": "没有准备好电子邮件地址?没关系!您可以跳过此步骤,稍后从项目面板邀请团队成员。", + + "orgCategoryTech": "科技公司", + "orgCategoryCreative": "创意机构", + "orgCategoryConsulting": "咨询公司", + "orgCategoryStartups": "初创企业", + "namingTip1": "保持简单且易记", + "namingTip2": "体现您的行业或价值观", + "namingTip3": "考虑未来发展", + "namingTip4": "使其独特且有品牌感", + + "aboutYouTitle": "告诉我们关于您的信息", + "aboutYouDescription": "帮助我们个性化您的体验", + "orgTypeQuestion": "哪项最能描述您的组织?", + "userRoleQuestion": "您的角色是什么?", + + "yourNeedsTitle": "您的主要需求是什么?", + "yourNeedsDescription": "请选择所有适用项,帮助我们设置您的工作区", + "yourNeedsQuestion": "您主要如何使用 Worklenz?", + "useCaseTaskOrg": "组织和跟踪任务", + "useCaseTeamCollab": "团队协作", + "useCaseResourceMgmt": "管理时间和资源", + "useCaseClientComm": "与客户保持联系", + "useCaseTimeTrack": "监控项目工时", + "useCaseOther": "其他", + "selectedText": "已选择", + "previousToolsQuestion": "您之前用过哪些工具?(可选)", + "previousToolsPlaceholder": "例如:Asana、Trello、Jira、Monday.com 等", + + "discoveryTitle": "最后一个问题……", + "discoveryDescription": "帮助我们了解您是如何发现 Worklenz 的", + "discoveryQuestion": "您是如何听说我们的?", + "allSetTitle": "一切就绪!", + "allSetDescription": "让我们创建您的第一个项目并开始使用 Worklenz 吧", + "aboutYouStepName": "关于您", + "yourNeedsStepName": "您的需求", + "discoveryStepName": "发现", + "stepProgress": "第 {step} 步,共 3 步:{title}", + + "projectStepHeader": "让我们创建您的第一个项目", + "projectStepSubheader": "从头开始或使用模板更快上手", + "startFromScratch": "从头开始", + "templateSelected": "已选择模板如下", + "quickSuggestions": "快速建议:", + "orText": "或", + "startWithTemplate": "从模板开始", + "clearToSelectTemplate": "请先清空上方项目名称以选择模板", + "templateHeadStart": "使用预设项目结构快速开始", + "browseAllTemplates": "浏览所有模板", + "templatesAvailable": "15+ 行业专用模板可用", + "chooseTemplate": "选择与您的项目类型匹配的模板", + "createProject": "创建项目", + + "templateSoftwareDev": "软件开发", + "templateSoftwareDesc": "敏捷冲刺、缺陷跟踪、版本发布", + "templateMarketing": "市场营销活动", + "templateMarketingDesc": "活动策划、内容日历", + "templateConstruction": "建设项目", + "templateConstructionDesc": "阶段、许可、承包商", + "templateStartup": "初创启动", + "templateStartupDesc": "MVP 开发、融资、增长", + + "tasksStepTitle": "添加您的第一个任务", + "tasksStepDescription": "将 \"{projectName}\" 拆分为可执行任务以开始", + "taskPlaceholder": "任务 {index} - 例如:需要做什么?", + "addAnotherTask": "添加另一个任务 ({current}/{max})", "surveyStepTitle": "告诉我们关于您的信息", - "surveyStepLabel": "通过回答几个问题帮助我们个性化您的Worklenz体验。", - - "organizationType": "什么最能描述您的组织?", + "surveyStepLabel": "通过回答几个问题帮助我们个性化您的 Worklenz 体验。", + + "organizationType": "哪项最能描述您的组织?", "organizationTypeFreelancer": "自由职业者", "organizationTypeStartup": "初创公司", "organizationTypeSmallMediumBusiness": "中小企业", "organizationTypeAgency": "代理机构", "organizationTypeEnterprise": "企业", "organizationTypeOther": "其他", - + "userRole": "您的角色是什么?", - "userRoleFounderCeo": "创始人/CEO", + "userRoleFounderCeo": "创始人 / CEO", "userRoleProjectManager": "项目经理", "userRoleSoftwareDeveloper": "软件开发者", "userRoleDesigner": "设计师", "userRoleOperations": "运营", "userRoleOther": "其他", - - "mainUseCases": "您主要将Worklenz用于什么?", + + "mainUseCases": "您主要将 Worklenz 用于什么?", "mainUseCasesTaskManagement": "任务管理", "mainUseCasesTeamCollaboration": "团队协作", "mainUseCasesResourcePlanning": "资源规划", - "mainUseCasesClientCommunication": "客户沟通和报告", + "mainUseCasesClientCommunication": "客户沟通与报告", "mainUseCasesTimeTracking": "时间跟踪", "mainUseCasesOther": "其他", - - "previousTools": "您在使用Worklenz之前使用什么工具?", + + "previousTools": "在使用 Worklenz 之前您用过哪些工具?", "previousToolsPlaceholder": "例如:Trello、Asana、Monday.com", - - "howHeardAbout": "您是如何了解Worklenz的?", - "howHeardAboutGoogleSearch": "Google搜索", + + "howHeardAbout": "您是如何了解 Worklenz 的?", + "howHeardAboutGoogleSearch": "Google 搜索", "howHeardAboutTwitter": "Twitter", "howHeardAboutLinkedin": "LinkedIn", "howHeardAboutFriendColleague": "朋友或同事", "howHeardAboutBlogArticle": "博客或文章", "howHeardAboutOther": "其他" -} \ No newline at end of file +} diff --git a/worklenz-frontend/src/api/survey/survey.api.service.ts b/worklenz-frontend/src/api/survey/survey.api.service.ts index 381958f0..f4c29509 100644 --- a/worklenz-frontend/src/api/survey/survey.api.service.ts +++ b/worklenz-frontend/src/api/survey/survey.api.service.ts @@ -2,7 +2,7 @@ import { IServerResponse } from '@/types/common.types'; import { ISurvey, ISurveySubmissionRequest, ISurveyResponse } from '@/types/account-setup/survey.types'; import apiClient from '../api-client'; -const API_BASE_URL = '/api'; +const API_BASE_URL = '/api/v1'; export const surveyApiService = { async getAccountSetupSurvey(): Promise> { diff --git a/worklenz-frontend/src/components/account-setup/members-step.tsx b/worklenz-frontend/src/components/account-setup/members-step.tsx index d3feedc1..7fe12da5 100644 --- a/worklenz-frontend/src/components/account-setup/members-step.tsx +++ b/worklenz-frontend/src/components/account-setup/members-step.tsx @@ -1,16 +1,15 @@ -import React, { useEffect, useRef } from 'react'; -import { Form, Input, Button, List, Alert, message, InputRef } from '@/shared/antd-imports'; -import { CloseCircleOutlined, MailOutlined, PlusOutlined } from '@/shared/antd-imports'; +import React, { useEffect, useRef, useState } from 'react'; +import { Form, Input, Button, Typography, Card, Avatar, Tag, Alert, Space, Dropdown, MenuProps } from '@/shared/antd-imports'; +import { CloseCircleOutlined, MailOutlined, PlusOutlined, UserOutlined, CheckCircleOutlined, ExclamationCircleOutlined, GlobalOutlined } from '@/shared/antd-imports'; import { useTranslation } from 'react-i18next'; -import { Typography } from '@/shared/antd-imports'; -import { setTeamMembers, setTasks } from '@/features/account-setup/account-setup.slice'; +import { setTeamMembers } from '@/features/account-setup/account-setup.slice'; import { useDispatch, useSelector } from 'react-redux'; import { RootState } from '@/app/store'; import { validateEmail } from '@/utils/validateEmail'; import { sanitizeInput } from '@/utils/sanitizeInput'; -import { Rule } from 'antd/es/form'; +import { setLanguage } from '@/features/i18n/localesSlice'; -const { Title } = Typography; +const { Title, Paragraph, Text } = Typography; interface Email { id: number; @@ -20,25 +19,57 @@ interface Email { interface MembersStepProps { isDarkMode: boolean; styles: any; + token?: any; } -const MembersStep: React.FC = ({ isDarkMode, styles }) => { - const { t } = useTranslation('account-setup'); +// Common email suggestions based on organization +const getEmailSuggestions = (orgName?: string) => { + if (!orgName) return []; + + const cleanOrgName = orgName.toLowerCase().replace(/[^a-z0-9]/g, ''); + const suggestions = [ + `info@${cleanOrgName}.com`, + `team@${cleanOrgName}.com`, + `hello@${cleanOrgName}.com`, + `contact@${cleanOrgName}.com` + ]; + + return suggestions; +}; + +// Role suggestions for team members +const roleSuggestions = [ + { role: 'Designer', icon: '🎨', description: 'UI/UX, Graphics, Creative' }, + { role: 'Developer', icon: '💻', description: 'Frontend, Backend, Full-stack' }, + { role: 'Project Manager', icon: '📊', description: 'Planning, Coordination' }, + { role: 'Marketing', icon: '📢', description: 'Content, Social Media, Growth' }, + { role: 'Sales', icon: '💼', description: 'Business Development, Client Relations' }, + { role: 'Operations', icon: '⚙️', description: 'Admin, HR, Finance' } +]; + +const MembersStep: React.FC = ({ isDarkMode, styles, token }) => { + const { t, i18n } = useTranslation('account-setup'); const { teamMembers, organizationName } = useSelector( (state: RootState) => state.accountSetupReducer ); - const inputRefs = useRef<(InputRef | null)[]>([]); + const { language } = useSelector((state: RootState) => state.localesReducer); + const inputRefs = useRef<(HTMLInputElement | null)[]>([]); const dispatch = useDispatch(); - const [form] = Form.useForm(); + const [focusedIndex, setFocusedIndex] = useState(null); + const [showSuggestions, setShowSuggestions] = useState(false); + const [validatedEmails, setValidatedEmails] = useState>(new Set()); + + const emailSuggestions = getEmailSuggestions(organizationName); const addEmail = () => { - if (teamMembers.length == 5) return; + if (teamMembers.length >= 5) return; const newId = teamMembers.length > 0 ? Math.max(...teamMembers.map(t => t.id)) + 1 : 0; dispatch(setTeamMembers([...teamMembers, { id: newId, value: '' }])); setTimeout(() => { - inputRefs.current[newId]?.focus(); - }, 0); + const newIndex = teamMembers.length; + inputRefs.current[newIndex]?.focus(); + }, 100); }; const removeEmail = (id: number) => { @@ -58,125 +89,223 @@ const MembersStep: React.FC = ({ isDarkMode, styles }) => { ); }; - const handleKeyPress = (e: React.KeyboardEvent) => { - const input = e.currentTarget as HTMLInputElement; - if (!input.value.trim()) return; - e.preventDefault(); - addEmail(); + const handleKeyPress = (e: React.KeyboardEvent, index: number) => { + if (e.key === 'Enter') { + const input = e.currentTarget as HTMLInputElement; + if (input.value.trim() && validateEmail(input.value.trim())) { + e.preventDefault(); + if (index === teamMembers.length - 1 && teamMembers.length < 5) { + addEmail(); + } else if (index < teamMembers.length - 1) { + inputRefs.current[index + 1]?.focus(); + } + } + } }; - // Function to set ref that doesn't return anything (void) - const setInputRef = (index: number) => (el: InputRef | null) => { - inputRefs.current[index] = el; + const handleSuggestionClick = (suggestion: string) => { + const emptyEmailIndex = teamMembers.findIndex(member => !member.value.trim()); + if (emptyEmailIndex !== -1) { + updateEmail(teamMembers[emptyEmailIndex].id, suggestion); + } else if (teamMembers.length < 5) { + const newId = teamMembers.length > 0 ? Math.max(...teamMembers.map(t => t.id)) + 1 : 0; + dispatch(setTeamMembers([...teamMembers, { id: newId, value: suggestion }])); + } + setShowSuggestions(false); }; useEffect(() => { setTimeout(() => { - inputRefs.current[teamMembers.length - 1]?.focus(); - // Set initial form values - const initialValues: Record = {}; - teamMembers.forEach(teamMember => { - initialValues[`email-${teamMember.id}`] = teamMember.value; - }); - form.setFieldsValue(initialValues); + inputRefs.current[0]?.focus(); }, 200); }, []); - const formRules = { - email: [ - { - validator: async (_: any, value: string) => { - if (!value) return; - if (!validateEmail(value)) { - throw new Error(t('invalidEmail')); - } - }, - }, - ], + const getEmailStatus = (email: string, memberId: number) => { + if (!email.trim()) return 'empty'; + if (!validatedEmails.has(memberId)) return 'empty'; + if (validateEmail(email)) return 'valid'; + return 'invalid'; }; + const handleBlur = (memberId: number, email: string) => { + setFocusedIndex(null); + if (email.trim()) { + setValidatedEmails(prev => new Set(prev).add(memberId)); + } + }; + + const languages = [ + { key: 'en', label: 'English', flag: '🇺🇸' }, + { key: 'es', label: 'Español', flag: '🇪🇸' }, + { key: 'pt', label: 'Português', flag: '🇵🇹' }, + { key: 'de', label: 'Deutsch', flag: '🇩🇪' }, + { key: 'alb', label: 'Shqip', flag: '🇦🇱' }, + { key: 'zh', label: '简体中文', flag: '🇨🇳' } + ]; + + const handleLanguageChange = (languageKey: string) => { + dispatch(setLanguage(languageKey)); + i18n.changeLanguage(languageKey); + }; + + const languageMenuItems: MenuProps['items'] = languages.map(lang => ({ + key: lang.key, + label: ( +
+ {lang.flag} + {lang.label} +
+ ), + onClick: () => handleLanguageChange(lang.key) + })); + + const currentLanguage = languages.find(lang => lang.key === language) || languages[0]; + return ( -
- - - {t('step3Title')} "<mark>{organizationName}</mark>". + <div className="w-full members-step"> + {/* Header */} + <div className="text-center mb-8"> + <Title level={3} className="mb-2" style={{ color: token?.colorText }}> + {t('membersStepTitle')} - - - {t('step3InputLabel')}  {t('maxMembers')} - - } - > - ( - -
- + + {t('membersStepDescription', { organizationName })} + +
+ + {/* Team Members List */} +
+
+ {teamMembers.map((teamMember, index) => { + const emailStatus = getEmailStatus(teamMember.value, teamMember.id); + return ( +
+ : + emailStatus === 'invalid' ? : + + } + /> + +
updateEmail(teamMember.id, e.target.value)} - onPressEnter={handleKeyPress} - ref={setInputRef(index)} - status={teamMember.value && !validateEmail(teamMember.value) ? 'error' : ''} - id={`member-${index}`} + onKeyPress={e => handleKeyPress(e, index)} + onFocus={() => setFocusedIndex(index)} + onBlur={() => handleBlur(teamMember.id, teamMember.value)} + ref={el => inputRefs.current[index] = el} + className="border-0 shadow-none" + style={{ + backgroundColor: 'transparent', + color: token?.colorText + }} + prefix={} + status={emailStatus === 'invalid' ? 'error' : undefined} + suffix={ + emailStatus === 'valid' ? ( + + ) : emailStatus === 'invalid' ? ( + + ) : null + } /> - -
+ + {teamMembers.length > 1 && ( +
- - )} - /> - -
+ + {/* Add Member Button */} + {teamMembers.length < 5 && ( + + )} +
+ + {/* Skip Option */} +
+
- - + /> +
+ + {/* Language Switcher */} +
+ + + +
+
); }; -export default MembersStep; +export default MembersStep; \ No newline at end of file diff --git a/worklenz-frontend/src/components/account-setup/organization-step.tsx b/worklenz-frontend/src/components/account-setup/organization-step.tsx index ccddc35e..2663e64c 100644 --- a/worklenz-frontend/src/components/account-setup/organization-step.tsx +++ b/worklenz-frontend/src/components/account-setup/organization-step.tsx @@ -1,5 +1,5 @@ -import React, { useEffect, useRef } from 'react'; -import { Form, Input, InputRef, Typography } from '@/shared/antd-imports'; +import React, { useEffect, useRef, useState } from 'react'; +import { Form, Input, InputRef, Typography, Card, Row, Col, Tag, Tooltip, Button } from '@/shared/antd-imports'; import { useDispatch, useSelector } from 'react-redux'; import { useTranslation } from 'react-i18next'; import { setOrganizationName } from '@/features/account-setup/account-setup.slice'; @@ -7,25 +7,40 @@ import { RootState } from '@/app/store'; import { sanitizeInput } from '@/utils/sanitizeInput'; import './admin-center-common.css'; -const { Title } = Typography; +const { Title, Paragraph, Text } = Typography; interface Props { onEnter: () => void; styles: any; organizationNamePlaceholder: string; organizationNameInitialValue?: string; + isDarkMode: boolean; + token?: any; } +// Organization name suggestions by type +const organizationSuggestions = [ + { category: 'Tech Companies', examples: ['TechCorp', 'DevStudio', 'CodeCraft', 'PixelForge'] }, + { category: 'Creative Agencies', examples: ['Creative Hub', 'Design Studio', 'Brand Works', 'Visual Arts'] }, + { category: 'Consulting', examples: ['Strategy Group', 'Business Solutions', 'Expert Advisors', 'Growth Partners'] }, + { category: 'Startups', examples: ['Innovation Labs', 'Future Works', 'Venture Co', 'Next Gen'] }, +]; + + export const OrganizationStep: React.FC = ({ onEnter, styles, organizationNamePlaceholder, organizationNameInitialValue, + isDarkMode, + token, }) => { const { t } = useTranslation('account-setup'); const dispatch = useDispatch(); const { organizationName } = useSelector((state: RootState) => state.accountSetupReducer); const inputRef = useRef(null); + const [showSuggestions, setShowSuggestions] = useState(false); + const [selectedCategory, setSelectedCategory] = useState(null); // Autofill organization name if not already set useEffect(() => { @@ -45,26 +60,180 @@ export const OrganizationStep: React.FC = ({ dispatch(setOrganizationName(sanitizedValue)); }; + const handleSuggestionClick = (suggestion: string) => { + dispatch(setOrganizationName(suggestion)); + inputRef.current?.focus(); + setShowSuggestions(false); + }; + + const toggleSuggestions = () => { + setShowSuggestions(!showSuggestions); + }; + return ( -
- - - {t('organizationStepTitle')} + <div className="w-full organization-step"> + {/* Header */} + <div className="text-center mb-8"> + <Title level={3} className="mb-2" style={{ color: token?.colorText }}> + {t('organizationStepWelcome')} - - {t('organizationStepLabel')}} + + {t('organizationStepDescription')} + + + + {/* Main Form Card */} +
+ + + + {t('organizationStepLabel')} + + + + ⓘ + + +
+ } + > + +
+ + {/* Quick Actions */} +
+ + {organizationNameInitialValue && organizationNameInitialValue !== organizationName && ( + + )} +
+ + {/* Character Count and Validation */} +
+ + {organizationName.length}/50 {t('organizationStepCharacters')} + + {organizationName.length > 0 && ( +
+ {organizationName.length >= 2 ? ( + ✓ {t('organizationStepGoodLength')} + ) : ( + ⚠ {t('organizationStepTooShort')} + )} +
+ )} +
+ + + + + {/* Suggestions Panel */} + {showSuggestions && ( +
+ + 🎯 + {t('organizationStepSuggestionsTitle')} +
+ } + style={{ backgroundColor: token?.colorBgContainer }} + > +
+ {organizationSuggestions.map((category, categoryIndex) => ( +
+
+ setSelectedCategory( + selectedCategory === category.category ? null : category.category + )} + > + {t(`organizationStepCategory${categoryIndex + 1}`, category.category)} + +
+
+ {category.examples.map((example, exampleIndex) => ( + + ))} +
+
+ ))} +
+ +
+ + 💡 {t('organizationStepSuggestionsNote')} + +
+ + + )} + + {/* Footer Note */} +
- - - + + 🔒 {t('organizationStepPrivacyNote')} + +
+ ); -}; +}; \ No newline at end of file diff --git a/worklenz-frontend/src/components/account-setup/project-step.tsx b/worklenz-frontend/src/components/account-setup/project-step.tsx index 1447bfd8..de2257a1 100644 --- a/worklenz-frontend/src/components/account-setup/project-step.tsx +++ b/worklenz-frontend/src/components/account-setup/project-step.tsx @@ -3,7 +3,7 @@ import { useSelector } from 'react-redux'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; -import { Button, Drawer, Form, Input, InputRef, Select, Typography } from '@/shared/antd-imports'; +import { Button, Drawer, Form, Input, InputRef, Typography, Card, Row, Col, Tag, Tooltip } from '@/shared/antd-imports'; import TemplateDrawer from '../common/template-drawer/template-drawer'; import { RootState } from '@/app/store'; @@ -24,15 +24,62 @@ import { setUser } from '@/features/user/userSlice'; import { setSession } from '@/utils/session-helper'; import { IAuthorizeResponse } from '@/types/auth/login.types'; -const { Title } = Typography; +const { Title, Paragraph, Text } = Typography; interface Props { onEnter: () => void; styles: any; isDarkMode: boolean; + token?: any; } -export const ProjectStep: React.FC = ({ onEnter, styles, isDarkMode = false }) => { +// Popular template suggestions +const templateSuggestions = [ + { + id: 'software', + title: 'Software Development', + icon: '💻', + description: 'Agile sprints, bug tracking, releases', + tags: ['Agile', 'Scrum', 'Development'] + }, + { + id: 'marketing', + title: 'Marketing Campaign', + icon: '📢', + description: 'Campaign planning, content calendar', + tags: ['Content', 'Social Media', 'Analytics'] + }, + { + id: 'construction', + title: 'Construction Project', + icon: '🏗️', + description: 'Phases, permits, contractors', + tags: ['Planning', 'Execution', 'Inspection'] + }, + { + id: 'startup', + title: 'Startup Launch', + icon: '🚀', + description: 'MVP development, funding, growth', + tags: ['MVP', 'Funding', 'Launch'] + } +]; + +// Project name suggestions based on organization type +const getProjectSuggestions = (orgType?: string) => { + const suggestions: Record = { + 'freelancer': ['Client Website', 'Logo Design', 'Content Writing', 'App Development'], + 'startup': ['MVP Development', 'Product Launch', 'Marketing Campaign', 'Investor Pitch'], + 'small_medium_business': ['Q1 Sales Initiative', 'Website Redesign', 'Process Improvement', 'Team Training'], + 'agency': ['Client Campaign', 'Brand Strategy', 'Website Project', 'Creative Brief'], + 'enterprise': ['Digital Transformation', 'System Migration', 'Annual Planning', 'Department Initiative'], + 'other': ['New Project', 'Team Initiative', 'Q1 Goals', 'Special Project'] + }; + + return suggestions[orgType || 'other'] || suggestions['other']; +}; + +export const ProjectStep: React.FC = ({ onEnter, styles, isDarkMode = false, token }) => { const { t } = useTranslation('account-setup'); const dispatch = useAppDispatch(); const navigate = useNavigate(); @@ -44,11 +91,15 @@ export const ProjectStep: React.FC = ({ onEnter, styles, isDarkMode = fal setTimeout(() => inputRef.current?.focus(), 200); }, []); - const { projectName, templateId, organizationName } = useSelector( + + const { projectName, templateId, organizationName, surveyData } = useSelector( (state: RootState) => state.accountSetupReducer ); const [open, setOpen] = useState(false); const [creatingFromTemplate, setCreatingFromTemplate] = useState(false); + const [selectedTemplate, setSelectedTemplate] = useState(templateId || null); + + const projectSuggestions = getProjectSuggestions(surveyData.organization_type); const handleTemplateSelected = (templateId: string) => { if (!templateId) return; @@ -103,43 +154,215 @@ export const ProjectStep: React.FC = ({ onEnter, styles, isDarkMode = fal dispatch(setProjectName(sanitizedValue)); }; + const handleProjectNameFocus = () => { + // Clear template selection when user focuses on project name input + if (templateId) { + dispatch(setTemplateId(null)); + setSelectedTemplate(null); + } + }; + + const handleSuggestionClick = (suggestion: string) => { + dispatch(setProjectName(suggestion)); + inputRef.current?.focus(); + }; + return ( -
-
- - - {t('projectStepTitle')} - - - {t('projectStepLabel')}} - > - - -
-
- - {t('or')} + <div className="w-full project-step"> + {/* Header */} + <div className="text-center mb-8"> + <Title level={3} className="mb-2" style={{ color: token?.colorText }}> + Let's create your first project -
+ + Start from scratch or use a template to get going faster +
-
- + {/* Project Name Section */} +
+ +
+
+ + Start from scratch + + {templateId && ( + + Template selected below + + )} +
+
+ + {t('projectStepLabel')}} + > + + + + {/* Quick suggestions */} +
+ + Quick suggestions: + +
+ {projectSuggestions.map((suggestion, index) => ( + + ))} +
+
+
+ + {/* OR Divider */} +
+
+
+
+
+ + OR + +
+
+ + {/* Template Section */} +
+
+ + Start with a template + + + {projectName?.trim() + ? "Clear project name above to select a template" + : "Get a head start with pre-built project structures" + } + +
+ + {/* Template Preview Cards */} + + {templateSuggestions.map((template) => ( + + { + if (projectName?.trim()) return; // Don't allow selection if project name is entered + setSelectedTemplate(template.id); + dispatch(setTemplateId(template.id)); + }} + > +
+ {template.icon} +
+ + {template.title} + + + {template.description} + +
+ {template.tags.map((tag, index) => ( + + {tag} + + ))} +
+
+
+
+ + ))} +
+ + {/* Browse All Templates Button */} +
+ +
+ + 15+ industry-specific templates available + +
+
+
+ + {/* Template Drawer */} {createPortal( + + {t('templateDrawerTitle')} + + + Choose a template that matches your project type + +
+ } width={1000} onClose={() => toggleTemplateSelector(false)} open={open} @@ -152,11 +375,13 @@ export const ProjectStep: React.FC = ({ onEnter, styles, isDarkMode = fal type="primary" onClick={() => createFromTemplate()} loading={creatingFromTemplate} + disabled={!templateId} > - {t('create')} + {t('create')} Project
} + style={{ backgroundColor: token?.colorBgLayout }} > = ({ onEnter, styles, isDarkMode = fal )}
); -}; +}; \ No newline at end of file diff --git a/worklenz-frontend/src/components/account-setup/survey-step.tsx b/worklenz-frontend/src/components/account-setup/survey-step.tsx index e3fa7d25..1e3dd3bd 100644 --- a/worklenz-frontend/src/components/account-setup/survey-step.tsx +++ b/worklenz-frontend/src/components/account-setup/survey-step.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import { Form, Input, Typography, Button } from '@/shared/antd-imports'; +import { Form, Input, Typography, Button, Progress, Space } from '@/shared/antd-imports'; import { useDispatch, useSelector } from 'react-redux'; import { useTranslation } from 'react-i18next'; -import { setSurveyData } from '@/features/account-setup/account-setup.slice'; +import { setSurveyData, setSurveySubStep } from '@/features/account-setup/account-setup.slice'; import { RootState } from '@/app/store'; import { OrganizationType, @@ -12,7 +12,7 @@ import { IAccountSetupSurveyData } from '@/types/account-setup/survey.types'; -const { Title } = Typography; +const { Title, Paragraph } = Typography; const { TextArea } = Input; interface Props { @@ -22,162 +22,244 @@ interface Props { token?: any; } -export const SurveyStep: React.FC = ({ onEnter, styles, isDarkMode, token }) => { +interface SurveyPageProps { + styles: any; + isDarkMode: boolean; + token?: any; + surveyData: IAccountSetupSurveyData; + handleSurveyDataChange: (field: keyof IAccountSetupSurveyData, value: any) => void; + handleUseCaseToggle?: (value: UseCase) => void; +} + +// Page 1: About You +const AboutYouPage: React.FC = ({ styles, token, surveyData, handleSurveyDataChange }) => { const { t } = useTranslation('account-setup'); - const dispatch = useDispatch(); - const { surveyData } = useSelector((state: RootState) => state.accountSetupReducer); - - const handleSurveyDataChange = (field: keyof IAccountSetupSurveyData, value: any) => { - dispatch(setSurveyData({ [field]: value })); - }; - - // Get Ant Design button type based on selection state - const getButtonType = (isSelected: boolean) => { - return isSelected ? 'primary' : 'default'; - }; - - // Handle multi-select for use cases (button-based) - const handleUseCaseToggle = (value: UseCase) => { - const currentUseCases = surveyData.main_use_cases || []; - const isSelected = currentUseCases.includes(value); - - let newUseCases; - if (isSelected) { - // Remove if already selected - newUseCases = currentUseCases.filter(useCase => useCase !== value); - } else { - // Add if not selected - newUseCases = [...currentUseCases, value]; - } - - handleSurveyDataChange('main_use_cases', newUseCases); - }; - - const onPressEnter = () => { - onEnter(); - }; - - const organizationTypeOptions: { value: OrganizationType; label: string }[] = [ - { value: 'freelancer', label: t('organizationTypeFreelancer') }, - { value: 'startup', label: t('organizationTypeStartup') }, - { value: 'small_medium_business', label: t('organizationTypeSmallMediumBusiness') }, - { value: 'agency', label: t('organizationTypeAgency') }, - { value: 'enterprise', label: t('organizationTypeEnterprise') }, - { value: 'other', label: t('organizationTypeOther') }, + + const organizationTypeOptions: { value: OrganizationType; label: string; icon?: string }[] = [ + { value: 'freelancer', label: t('organizationTypeFreelancer'), icon: '👤' }, + { value: 'startup', label: t('organizationTypeStartup'), icon: '🚀' }, + { value: 'small_medium_business', label: t('organizationTypeSmallMediumBusiness'), icon: '🏢' }, + { value: 'agency', label: t('organizationTypeAgency'), icon: '🎯' }, + { value: 'enterprise', label: t('organizationTypeEnterprise'), icon: '🏛️' }, + { value: 'other', label: t('organizationTypeOther'), icon: '📋' }, ]; - const userRoleOptions: { value: UserRole; label: string }[] = [ - { value: 'founder_ceo', label: t('userRoleFounderCeo') }, - { value: 'project_manager', label: t('userRoleProjectManager') }, - { value: 'software_developer', label: t('userRoleSoftwareDeveloper') }, - { value: 'designer', label: t('userRoleDesigner') }, - { value: 'operations', label: t('userRoleOperations') }, - { value: 'other', label: t('userRoleOther') }, - ]; - - const useCaseOptions: { value: UseCase; label: string }[] = [ - { value: 'task_management', label: t('mainUseCasesTaskManagement') }, - { value: 'team_collaboration', label: t('mainUseCasesTeamCollaboration') }, - { value: 'resource_planning', label: t('mainUseCasesResourcePlanning') }, - { value: 'client_communication', label: t('mainUseCasesClientCommunication') }, - { value: 'time_tracking', label: t('mainUseCasesTimeTracking') }, - { value: 'other', label: t('mainUseCasesOther') }, - ]; - - const howHeardAboutOptions: { value: HowHeardAbout; label: string }[] = [ - { value: 'google_search', label: t('howHeardAboutGoogleSearch') }, - { value: 'twitter', label: t('howHeardAboutTwitter') }, - { value: 'linkedin', label: t('howHeardAboutLinkedin') }, - { value: 'friend_colleague', label: t('howHeardAboutFriendColleague') }, - { value: 'blog_article', label: t('howHeardAboutBlogArticle') }, - { value: 'other', label: t('howHeardAboutOther') }, + const userRoleOptions: { value: UserRole; label: string; icon?: string }[] = [ + { value: 'founder_ceo', label: t('userRoleFounderCeo'), icon: '👔' }, + { value: 'project_manager', label: t('userRoleProjectManager'), icon: '📊' }, + { value: 'software_developer', label: t('userRoleSoftwareDeveloper'), icon: '💻' }, + { value: 'designer', label: t('userRoleDesigner'), icon: '🎨' }, + { value: 'operations', label: t('userRoleOperations'), icon: '⚙️' }, + { value: 'other', label: t('userRoleOther'), icon: '✋' }, ]; return ( -
- - - {t('surveyStepTitle')} + <div className="w-full"> + <div className="text-center mb-8"> + <Title level={3} className="mb-2" style={{ color: token?.colorText }}> + Tell us about yourself -

- {t('surveyStepLabel')} -

-
+ + Help us personalize your experience + + {/* Organization Type */} - {t('organizationType')}} - className="mb-6" - > -
- {organizationTypeOptions.map((option) => ( - - ))} -
-
- - {/* User Role */} - {t('userRole')}} - className="mb-6" - > -
- {userRoleOptions.map((option) => ( - - ))} -
-
- - {/* Main Use Cases */} - {t('mainUseCases')}} - className="mb-6" - > -
- {useCaseOptions.map((option) => { - const isSelected = (surveyData.main_use_cases || []).includes(option.value); + + +
+ {organizationTypeOptions.map((option) => { + const isSelected = surveyData.organization_type === option.value; return ( - +
+ {option.icon} + + {option.label} + +
+ ); })}
+ {/* User Role */} + + +
+ {userRoleOptions.map((option) => { + const isSelected = surveyData.user_role === option.value; + return ( + + ); + })} +
+
+
+ ); +}; + +// Page 2: Your Needs +const YourNeedsPage: React.FC = ({ styles, token, surveyData, handleSurveyDataChange, handleUseCaseToggle }) => { + const { t } = useTranslation('account-setup'); + + const useCaseOptions: { value: UseCase; label: string; description: string }[] = [ + { value: 'task_management', label: t('mainUseCasesTaskManagement'), description: 'Organize and track tasks' }, + { value: 'team_collaboration', label: t('mainUseCasesTeamCollaboration'), description: 'Work together seamlessly' }, + { value: 'resource_planning', label: t('mainUseCasesResourcePlanning'), description: 'Manage time and resources' }, + { value: 'client_communication', label: t('mainUseCasesClientCommunication'), description: 'Stay connected with clients' }, + { value: 'time_tracking', label: t('mainUseCasesTimeTracking'), description: 'Monitor project hours' }, + { value: 'other', label: t('mainUseCasesOther'), description: 'Something else' }, + ]; + + // Use the passed handler or fall back to regular handler + const onUseCaseClick = (value: UseCase) => { + if (handleUseCaseToggle) { + handleUseCaseToggle(value); + } else { + const currentUseCases = surveyData.main_use_cases || []; + const isSelected = currentUseCases.includes(value); + + let newUseCases; + if (isSelected) { + newUseCases = currentUseCases.filter(useCase => useCase !== value); + } else { + newUseCases = [...currentUseCases, value]; + } + + handleSurveyDataChange('main_use_cases', newUseCases); + } + }; + + return ( +
+
+ + What are your main needs? + + + Select all that apply to help us set up your workspace + +
+ + {/* Main Use Cases */} + + +
+ {useCaseOptions.map((option) => { + const isSelected = (surveyData.main_use_cases || []).includes(option.value); + return ( + + ); + })} +
+ {surveyData.main_use_cases && surveyData.main_use_cases.length > 0 && ( +

+ {surveyData.main_use_cases.length} selected +

+ )} +
+ {/* Previous Tools */} - {t('previousTools')}} - className="mb-6" - > + +