Skip to content

Commit

Permalink
STUD-223: Add country to signup flow and make it editable in profile (#…
Browse files Browse the repository at this point in the history
…609)

* STUD-223: Add country to signup flow and make it editable in profile

* STUD-223: Modify country validation

* STUD-223: Fix location redirect issue

* STUD-223: Add stagename and reinitialize to signup

* STUD-223: Add tooltip copy to location in profile
  • Loading branch information
escobarjonatan authored May 6, 2024
1 parent dfae82f commit 1244e75
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 29 deletions.
1 change: 1 addition & 0 deletions apps/studio/src/common/formUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export const commonYupValidation = {
.trim()
.max(20, "Must be 20 characters or less")
.required("Last name is required"),
location: Yup.string().required("This field is required"),
moods: Yup.array().max(5, "Maximum of 5 moods allowed"),
newPassword: Yup.string()
.test(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { FunctionComponent, useEffect, useRef } from "react";
import { Box, Stack, useTheme } from "@mui/material";
import { Box, Stack, Typography, useTheme } from "@mui/material";
import { FormikValues, useFormikContext } from "formik";
import {
Button,
FilteredTagsField,
GradientTextInputField,
Typography,
} from "@newm-web/elements";
import { useUserDevice, useWindowDimensions } from "@newm-web/utils";
import { ResponsiveNEWMLogo } from "../../components";
Expand Down Expand Up @@ -153,11 +152,7 @@ const AddProfileInformation: FunctionComponent<AddProfileInformationProps> = ({
Next
</Button>
</Stack>
<Typography
color="grey100"
sx={ { opacity: isValid ? 1 : 0.5 } }
variant="h5"
>
<Typography sx={ { opacity: isValid ? 1 : 0.5 } } variant="subtitle2">
or press Enter
</Typography>
</Box>
Expand Down
25 changes: 24 additions & 1 deletion apps/studio/src/modules/content/api.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
import { Genre, Language, Role } from "./types";
import { Country, Genre, Language, Role } from "./types";
import { Tags, newmApi } from "../../api";
import { setToastMessage } from "../../modules/ui";

export const extendedApi = newmApi.injectEndpoints({
endpoints: (build) => ({
getCountries: build.query<Array<string>, void>({
async onQueryStarted(body, { dispatch, queryFulfilled }) {
try {
await queryFulfilled;
} catch (error) {
dispatch(
setToastMessage({
message: "An error occurred while fetching countries",
severity: "error",
})
);
}
},

query: () => ({ method: "GET", url: "v1/distribution/countries" }),
transformResponse: (response: Country[]) => {
const extracted = response.map((country) => country.country_name);

// Sort alphabetically
return extracted.sort((a, b) => a.localeCompare(b));
},
}),
getGenres: build.query<string[], void>({
async onQueryStarted(body, { dispatch, queryFulfilled }) {
try {
Expand Down Expand Up @@ -103,6 +125,7 @@ export const extendedApi = newmApi.injectEndpoints({
});

export const {
useGetCountriesQuery,
useGetGenresQuery,
useGetISRCCountryCodesQuery,
useGetLanguagesQuery,
Expand Down
11 changes: 11 additions & 0 deletions apps/studio/src/modules/content/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,14 @@ export interface Language {
readonly language_code: string;
readonly language_name: string;
}

interface State {
readonly state_code: string;
readonly state_name: string;
}

export interface Country {
readonly country_code: string;
readonly country_name: string;
readonly state?: State[];
}
1 change: 1 addition & 0 deletions apps/studio/src/modules/session/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export interface ProfileFormValues
readonly email?: string;
readonly firstName?: string;
readonly lastName?: string;
readonly location?: string;
readonly pictureUrl?: string | File;
readonly role?: string;
}
Expand Down
28 changes: 24 additions & 4 deletions apps/studio/src/pages/createProfile/CreateProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,41 @@ import { FunctionComponent } from "react";
import { Box, Container, useTheme } from "@mui/material";
import { WizardForm } from "@newm-web/elements";
import * as Yup from "yup";
import { getUpdatedValues } from "@newm-web/utils";
import Begin from "./Begin";
import SelectNickname from "./SelectNickname";
import SelectRole from "./SelectRole";
import Complete from "./Complete";
import AddFirstName from "./AddFirstName";
import AddLastName from "./AddLastName";
import SelectLocation from "./SelectLocation";
import { useGetRolesQuery } from "../../modules/content";
import { commonYupValidation } from "../../common";
import {
ProfileFormValues,
emptyProfile,
useGetProfileQuery,
useUpdateInitialProfileThunk,
} from "../../modules/session";

const CreateProfile: FunctionComponent = () => {
const theme = useTheme();
const { data: roles = [] } = useGetRolesQuery();
const {
data: { firstName, lastName, role, location, nickname } = emptyProfile,
} = useGetProfileQuery();

const [updateInitialProfile] = useUpdateInitialProfileThunk();

/**
* Initial form values.
*/
const initialValues: ProfileFormValues = {
firstName: "",
lastName: "",
role: "",
firstName,
lastName,
location,
nickname,
role,
};

/**
Expand All @@ -36,6 +45,7 @@ const CreateProfile: FunctionComponent = () => {
const validations = {
firstName: commonYupValidation.firstName,
lastName: commonYupValidation.lastName,
location: commonYupValidation.location,
nickname: commonYupValidation.nickname,
role: commonYupValidation.role(roles),
};
Expand All @@ -44,7 +54,9 @@ const CreateProfile: FunctionComponent = () => {
* Submits the form when on the last route of the form.
*/
const handleSubmit = (values: ProfileFormValues) => {
updateInitialProfile({ ...values });
const updatedValues = getUpdatedValues(initialValues, values);

updateInitialProfile(updatedValues);
};

return (
Expand All @@ -61,6 +73,7 @@ const CreateProfile: FunctionComponent = () => {
>
<Container maxWidth="xl">
<WizardForm
enableReinitialize={ true }
initialValues={ initialValues }
rootPath="create-profile"
routes={ [
Expand Down Expand Up @@ -96,6 +109,13 @@ const CreateProfile: FunctionComponent = () => {
role: validations.role,
}),
},
{
element: <SelectLocation />,
path: "what-is-your-location",
validationSchema: Yup.object().shape({
location: validations.location,
}),
},
{
element: <Complete />,
path: "complete",
Expand Down
102 changes: 102 additions & 0 deletions apps/studio/src/pages/createProfile/SelectLocation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { FunctionComponent, useEffect } from "react";
import { Box, Stack, Typography } from "@mui/material";
import { Button, DropdownSelectField } from "@newm-web/elements";
import theme from "@newm-web/theme";
import { useWindowDimensions } from "@newm-web/utils";
import { useFormikContext } from "formik";
import { ResponsiveNEWMLogo } from "../../components";
import { ProfileFormValues } from "../../modules/session";
import { useGetCountriesQuery } from "../../modules/content";

const SelectLocation: FunctionComponent = () => {
const windowWidth = useWindowDimensions()?.width;
const { isValid, handleSubmit } = useFormikContext<ProfileFormValues>();
const { data: locations = [] } = useGetCountriesQuery();

/**
* Add an event listener to submit the form when enter is pressed.
* Without this Formik will only submit the form when enter is pressed
* while an input is focused.
*/
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const handleKeyDown = (event: any) => {
if (event.key === "Enter") {
event.preventDefault();
handleSubmit();
}
};

document.addEventListener("keydown", handleKeyDown);

return () => {
document.removeEventListener("keydown", handleKeyDown);
};
}, [handleSubmit]);

return (
<Box alignItems="center" display="flex" flexDirection="column">
<Box mb={ 4 }>
<ResponsiveNEWMLogo />
</Box>
<Typography
id="location-prompt"
sx={ { textAlign: "center" } }
variant="h1"
>
Finally, what is your country of residence?
</Typography>
<Typography sx={ { mb: 6.5, textAlign: "center" } } variant="subtitle1">
Providing this info will make it easier for streaming platforms to make
your music easier to find for your fans & listeners.
</Typography>

<DropdownSelectField
aria-labelledby="location-prompt"
name="location"
options={ locations }
placeholder="Select or search your country"
widthType="default"
/>

<Box sx={ { mt: [2, 2, 4], width: "100%" } }>
<Box
sx={ {
alignItems: "center",
display: "flex",
flexDirection: "column",
mt: 2,
width: "100%",
} }
>
<Stack
sx={ {
alignItems: "center",
display: ["flex", "flex", "block"],
flexDirection: ["column", "column", "row"],
gap: 2,
mb: 1,
width: "100%",
} }
>
<Button
type="submit"
width={
windowWidth && windowWidth > theme.breakpoints.values.md
? "compact"
: "default"
}
>
Next
</Button>
</Stack>
<Typography sx={ { opacity: isValid ? 1 : 0.5 } } variant="subtitle2">
or press Enter
</Typography>
</Box>
</Box>
</Box>
);
};

export default SelectLocation;
14 changes: 10 additions & 4 deletions apps/studio/src/pages/home/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,22 @@ const Home: FunctionComponent = () => {
const [isMobileOpen, setMobileOpen] = useState(false);

const {
data: { firstName = "", lastName = "", role } = emptyProfile,
data: { firstName = "", lastName = "", role, location } = emptyProfile,
isLoading,
} = useGetProfileQuery();

const hasBasicDetails = !!(firstName && lastName && role);
const hasBasicDetails = !!(firstName && lastName && role && location);

if (!hasBasicDetails && !isLoading) {
navigate("/create-profile");
if (!firstName || !lastName || !role) {
navigate("/create-profile");
return null;
}

return null;
if (!location) {
navigate("/create-profile/what-is-your-location");
return null;
}
}

if (isLoading) return null;
Expand Down
33 changes: 22 additions & 11 deletions apps/studio/src/pages/home/profile/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ import {
commonYupValidation,
useAppDispatch,
} from "../../../common";
import { useGetRolesQuery } from "../../../modules/content";
import {
useGetCountriesQuery,
useGetRolesQuery,
} from "../../../modules/content";
import {
ProfileFormValues,
UpdateProfileRequest,
Expand All @@ -58,6 +61,7 @@ const Profile: FunctionComponent = () => {
const dispatch = useAppDispatch();
const { state } = useLocation();

const locationRef = useRef<HTMLDivElement>(null);
const companyNameRef = useRef<HTMLInputElement>(null);
const emailRef = useRef<HTMLInputElement>(null);
const firstNameRef = useRef<HTMLInputElement>(null);
Expand All @@ -69,6 +73,7 @@ const Profile: FunctionComponent = () => {

const windowWidth = useWindowDimensions()?.width;
const { data: roles = [] } = useGetRolesQuery();
const { data: locations = [] } = useGetCountriesQuery();

const {
data: {
Expand Down Expand Up @@ -164,6 +169,7 @@ const Profile: FunctionComponent = () => {
ipi: commonYupValidation.ipi,
isni: commonYupValidation.isni,
lastName: commonYupValidation.lastName,
location: commonYupValidation.location,
nickname: commonYupValidation.nickname,
role: commonYupValidation.role(roles),
soundCloudProfile: Yup.string()
Expand Down Expand Up @@ -293,6 +299,7 @@ const Profile: FunctionComponent = () => {
>
{ ({ dirty, errors, handleReset, isSubmitting }) => {
scrollToError(errors, isSubmitting, [
{ element: locationRef.current, error: errors.location },
{ element: roleRef.current, error: errors.role },
{ element: firstNameRef.current, error: errors.firstName },
{ element: lastNameRef.current, error: errors.lastName },
Expand Down Expand Up @@ -357,6 +364,7 @@ const Profile: FunctionComponent = () => {
<Stack
alignItems={ ["center", "center", "flex-start"] }
gap={ 1 }
ref={ locationRef }
width="100%"
>
<Stack
Expand All @@ -377,16 +385,19 @@ const Profile: FunctionComponent = () => {
) : null }
</Stack>

{ location ? (
<TextInputField
disabled={ true }
isOptional={ false }
label="LOCATION"
name="location"
readOnly={ true }
type="text"
/>
) : null }
<DropdownSelectField
isOptional={ false }
label="LOCATION"
name="location"
options={ locations }
placeholder="Select or search your country"
tooltipText={
"Providing this info will make it easier " +
"for streaming platforms to make your music easier to find " +
"for your fans & listeners."
}
widthType="default"
/>
</Stack>
</Stack>

Expand Down
Loading

0 comments on commit 1244e75

Please sign in to comment.