diff --git a/src/client/index.ts b/src/client/index.ts index 2fc957e..b774b75 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -19,19 +19,19 @@ export interface SchemaAuth { export interface SchemaCreateUser { /** @maxLength 150 */ email: string - /** @maxLength 100 */ - first_name: string is_active?: boolean is_admin?: boolean /** @maxLength 100 */ - last_name: string + name: string /** - * @minLength 10 + * @minLength 8 * @maxLength 100 */ password: string + /** @maxLength 20 */ + phone: string /** - * @minLength 5 + * @minLength 3 * @maxLength 50 */ username: string @@ -80,6 +80,52 @@ export interface SchemaMovieListResponse { offset?: number } +export interface SchemaRegisterUser { + /** @maxLength 150 */ + address: string + /** @maxLength 150 */ + address2?: string + /** @maxLength 150 */ + card_address?: string + /** @maxLength 150 */ + card_address2?: string + /** @maxLength 100 */ + card_city?: string + /** @maxLength 50 */ + card_expiration?: string + /** @maxLength 50 */ + card_number?: string + /** @maxLength 100 */ + card_state?: string + /** @maxLength 50 */ + card_type?: string + /** @maxLength 20 */ + card_zip?: string + /** @maxLength 100 */ + city: string + /** @maxLength 150 */ + email: string + /** @maxLength 100 */ + name: string + need_promotion?: boolean + /** + * @minLength 8 + * @maxLength 100 + */ + password: string + /** @maxLength 20 */ + phone: string + /** @maxLength 100 */ + state: string + /** + * @minLength 3 + * @maxLength 50 + */ + username: string + /** @maxLength 20 */ + zip: string +} + export interface SchemaTokenResponse { access_token?: string msg?: string @@ -87,12 +133,47 @@ export interface SchemaTokenResponse { } export interface SchemaUpdateUser { + /** @maxLength 150 */ + address: string + /** @maxLength 150 */ + address2?: string + /** @maxLength 150 */ + card_address?: string + /** @maxLength 150 */ + card_address2?: string /** @maxLength 100 */ - first_name: string - is_active?: boolean - is_admin?: boolean + card_city?: string + /** @maxLength 50 */ + card_expiration?: string + /** @maxLength 50 */ + card_number?: string + /** @maxLength 100 */ + card_state?: string + /** @maxLength 50 */ + card_type?: string + /** @maxLength 20 */ + card_zip?: string /** @maxLength 100 */ - last_name: string + city: string + /** @maxLength 100 */ + name: string + need_promotion?: boolean + /** + * @minLength 8 + * @maxLength 100 + */ + password: string + /** @maxLength 20 */ + phone: string + /** @maxLength 100 */ + state: string + /** + * @minLength 3 + * @maxLength 50 + */ + username: string + /** @maxLength 20 */ + zip: string } export interface SchemaUpsertMovie { @@ -120,15 +201,36 @@ export interface SchemaUpsertMovie { } export interface SchemaUser { - created_at?: string email?: string - first_name?: string id: string is_active?: boolean is_admin?: boolean - last_name?: string - updated_at?: string + name?: string + username?: string +} + +export interface SchemaUserDetail { + address?: string + address2?: string + card_address?: string + card_address2?: string + card_city?: string + card_expiration?: string + card_number?: string + card_state?: string + card_type?: string + card_zip?: string + city?: string + email?: string + id?: string + is_active?: boolean + is_admin?: boolean + name?: string + need_promotion?: boolean + phone?: string + state?: string username?: string + zip?: string } export interface SchemaUserListResponse { @@ -512,6 +614,32 @@ export class Api< }) } auth = { + /** + * @description Activate a new user. + * + * @tags Auth + * @name V1AuthActivateCreate + * @summary activate + * @request POST:/api/v1/auth/activate + */ + v1AuthActivateCreate: ( + query?: { + /** id */ + id?: string + /** code */ + code?: string + }, + params: RequestParams = {} + ) => + this.request({ + path: `/api/v1/auth/activate`, + method: 'POST', + query: query, + type: ContentType.Json, + format: 'json', + ...params + }), + /** * @description Get current JWT. * @@ -574,6 +702,27 @@ export class Api< type: ContentType.Json, format: 'json', ...params + }), + + /** + * @description Register for a new user. + * + * @tags Auth + * @name V1AuthRegisterCreate + * @summary register + * @request POST:/api/v1/auth/register + */ + v1AuthRegisterCreate: ( + register: SchemaRegisterUser, + params: RequestParams = {} + ) => + this.request({ + path: `/api/v1/auth/register`, + method: 'POST', + body: register, + type: ContentType.Json, + format: 'json', + ...params }) } movie = { @@ -698,7 +847,7 @@ export class Api< * @secure */ v1UsersMeList: (params: RequestParams = {}) => - this.request({ + this.request({ path: `/api/v1/users/me`, method: 'GET', secure: true, diff --git a/src/hooks/useAuth.tsx b/src/hooks/useAuth.tsx index 725b248..d02f901 100644 --- a/src/hooks/useAuth.tsx +++ b/src/hooks/useAuth.tsx @@ -1,16 +1,16 @@ // from https://github.com/sanjay-arya/react-auth-demo // we use HttpOnly cookies from backend, not localStorage for security import { useRequest } from 'ahooks' -import type { HttpResponse, SchemaUser } from 'client' +import type { HttpResponse, SchemaUserDetail } from 'client' import type React from 'react' import { createContext, useContext, useMemo, useState } from 'react' import Backend from 'utils/service' export interface AuthContextValue { - user: SchemaUser | undefined + user: SchemaUserDetail | undefined loading: boolean refresh: () => void - refreshAsync?: () => Promise> + refreshAsync?: () => Promise> } const AuthContext = createContext({ user: undefined, @@ -19,7 +19,7 @@ const AuthContext = createContext({ }) const AuthProvider: React.FC = ({ children }) => { - const [user, setUser] = useState() + const [user, setUser] = useState() const { loading, refresh, refreshAsync } = useRequest( async () => Backend.user.v1UsersMeList(), { diff --git a/src/pages/Activate/index.tsx b/src/pages/Activate/index.tsx new file mode 100644 index 0000000..5147d45 --- /dev/null +++ b/src/pages/Activate/index.tsx @@ -0,0 +1,39 @@ +import { useRequest } from 'ahooks' +import PageContainer from 'components/PageContainer' +import { useNavigate, useSearchParams } from 'react-router-dom' +import Backend from 'utils/service' + +const Activate: React.FC = () => { + const navigate = useNavigate() + const [searchParams] = useSearchParams() + const id = searchParams.get('id') ?? '' + const code = searchParams.get('code') ?? '' + const { loading } = useRequest( + async () => + Backend.auth.v1AuthActivateCreate({ + id, + code + }), + { + onSuccess: () => { + navigate('/') + }, + onError: () => { + navigate('/register') + } + } + ) + return ( +
+

{loading ? 'Loading' : 'Redirecting'}

+
+ ) +} + +const Index: React.FC = () => ( + + + +) + +export default Index diff --git a/src/pages/EditProfile/index.tsx b/src/pages/EditProfile/index.tsx index ffaac67..3e80974 100644 --- a/src/pages/EditProfile/index.tsx +++ b/src/pages/EditProfile/index.tsx @@ -1,129 +1,400 @@ +import { useRequest } from 'ahooks' +import type { SchemaUserDetail } from 'client' import PageContainer from 'components/PageContainer' +import type React from 'react' import { useState } from 'react' -import Button from 'react-bootstrap/Button' - -interface UserProfile { - username: string - password: string - email: string - birthday: string -} +import { Form, Button, Col, Row, Accordion } from 'react-bootstrap' +import Backend from 'utils/service' const UserProfileForm: React.FC = () => { - const [userProfile, setUserProfile] = useState({ - username: 'user', - password: '12345', - email: 'example@email.com', - birthday: '2001-01-01' + // TODO: submit update user + const { run: submit } = useRequest(async () => null) + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + submit() + } + const [user, setUser] = useState() + const { loading } = useRequest(async () => Backend.user.v1UsersMeList(), { + onSuccess: res => { + setUser(res.data) + } }) - // Controls whether or not user can edit data - const [editingUser, setEditingUser] = useState(false) - const [editingPass, setEditingPass] = useState(false) - const [editingEmail, setEditingEmail] = useState(false) - const [editingBday, setEditingBday] = useState(false) + return ( + <> +
+

Edit Profile

+
+ +
+ + + Email + + + + Password + { + setUser(prevUser => ({ + ...prevUser, + password: e.target.value + })) + }} + /> + + + + + Name + { + setUser(prevUser => ({ + ...prevUser, + name: e.target.value + })) + }} + /> + - const handleInputChange = (field: keyof UserProfile, value: string) => { - setUserProfile(prevProfile => ({ - ...prevProfile, - [field]: value - })) - } + + Phone + { + setUser(prevUser => ({ + ...prevUser, + phone: e.target.value + })) + }} + /> + + + + Address + { + setUser(prevUser => ({ + ...prevUser, + address: e.target.value + })) + }} + /> + + + Address 2 + { + setUser(prevUser => ({ + ...prevUser, + address2: e.target.value + })) + }} + /> + + + + City + { + setUser(prevUser => ({ + ...prevUser, + city: e.target.value + })) + }} + /> + + + State + { + setUser(prevUser => ({ + ...prevUser, + state: e.target.value + })) + }} + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Zip + { + setUser(prevUser => ({ + ...prevUser, + zip: e.target.value + })) + }} + /> + + + + { + setUser(prevUser => ({ + ...prevUser, + need_promotion: e.target.checked + })) + }} + /> + + + + Payment Information + + + + Card Type + { + setUser(prevUser => ({ + ...prevUser, + card_type: e.target.value + })) + }} + > + + + + + + + - // Function to handle Saving all values at once - const handleSaveALL = () => { - setEditingUser(false) - setEditingPass(false) - setEditingEmail(false) - setEditingBday(false) - } + + Card Number + { + setUser(prevUser => ({ + ...prevUser, + card_number: e.target.value + })) + }} + /> + + + Card Expiration + { + setUser(prevUser => ({ + ...prevUser, + card_expiration: e.target.value + })) + }} + /> + + + + Address + { + setUser(prevUser => ({ + ...prevUser, + card_address: e.target.value + })) + }} + /> + + + Address 2 + { + setUser(prevUser => ({ + ...prevUser, + card_address2: e.target.value + })) + }} + /> + + + + City + { + setUser(prevUser => ({ + ...prevUser, + card_city: e.target.value + })) + }} + /> + + + State + { + setUser(prevUser => ({ + ...prevUser, + card_state: e.target.value + })) + }} + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Zip + { + setUser(prevUser => ({ + ...prevUser, + card_zip: e.target.value + })) + }} + /> + + + + + - return ( -
-

User Profile

-

🎥

-
-

Username:

- handleInputChange('username', e.target.value)} - readOnly={!editingUser} - /> - {editingUser ? ( - - ) : null} - {!editingUser && ( - - )} -
-
-

Password:

- handleInputChange('password', e.target.value)} - readOnly={!editingPass} - /> - {editingPass ? ( - - ) : null} - {!editingPass && ( - - )} -
-
-

Email:

- handleInputChange('email', e.target.value)} - readOnly={!editingEmail} - /> - {editingEmail ? ( - - ) : null} - {!editingEmail && ( - - )} -
-
-

Birthday:

- handleInputChange('birthday', e.target.value)} - readOnly={!editingBday} - /> - {editingBday ? ( - - ) : null} - {!editingBday && ( - - )} -
- -
+ + + +
+ + ) } diff --git a/src/pages/Register/index.tsx b/src/pages/Register/index.tsx index 9e6e895..50a69d2 100644 --- a/src/pages/Register/index.tsx +++ b/src/pages/Register/index.tsx @@ -159,6 +159,9 @@ const RegistrationForm = () => { + + + Payment Information diff --git a/src/routes.tsx b/src/routes.tsx index 0808ed7..72c7305 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,4 +1,5 @@ // import { ProtectedRoute } from 'components/ProtectedRoute' +import Activate from 'pages/Activate' import Checkout from 'pages/Checkout' import EditProfile from 'pages/EditProfile' import Login from 'pages/Login' @@ -29,6 +30,10 @@ const children: RouteObject[] = [ path: '/register', element: }, + { + path: '/activate', + element: + }, { path: '/profile/edit', element: