-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add documentation for web (#251)
- Loading branch information
Showing
12 changed files
with
470 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
--- | ||
title: "Calling API" | ||
--- | ||
|
||
## Overview | ||
|
||
Ship uses [TanStack Query](https://tanstack.com/query/latest) with our own axios library wrapper. | ||
All queries are located in the `/resources` folder, organized into sub-folders for each resource. | ||
|
||
TanStack Query helps easily fetch, cache, and update data. | ||
Each API endpoint has a related React hook using TanStack Query. | ||
|
||
When getting data with **apiService** in `services/api.service.ts`, no need to add the full endpoint URL. | ||
The axios instance already has the base URL set. | ||
|
||
If you need to make a request to a new endpoint, for example, for a `project` resource, | ||
you need to add a new hook to `/resources/project/project.api.ts` | ||
|
||
## Examples | ||
|
||
```typescript resources/account/account.api.ts | ||
export function useUpdate<T>() { | ||
const update = (data: T) => apiService.put("/account", data); | ||
|
||
return useMutation<User, unknown, T>(update); | ||
} | ||
``` | ||
|
||
```typescript resources/user/user.api.ts | ||
export function useList<T>(params: T) { | ||
const list = () => apiService.get("/users", params); | ||
|
||
interface UserListResponse { | ||
count: number; | ||
items: User[]; | ||
totalPages: number; | ||
} | ||
|
||
return useQuery<UserListResponse>(["users", params], list); | ||
} | ||
``` | ||
|
||
```typescript pages/profile/index.page.tsx | ||
type UpdateParams = z.infer<typeof accountUpdateSchema>; | ||
|
||
const { mutate: update, isLoading: isUpdateLoading } = accountApi.useUpdate<UpdateParams>(); | ||
``` | ||
|
||
```typescript pages/home/index.tsx | ||
const { data: users, isLoading: isUsersLoading } = userApi.useList(params); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
--- | ||
title: Environment Variables | ||
--- | ||
|
||
## Overview | ||
|
||
We use different environment variables for different stages like developing, testing, and when the app is live. | ||
|
||
This is done by using special files that have environments in them. | ||
|
||
### How the App Chooses the Right Environment Variables File | ||
The app knows which file to use based on a special environment variable called `APP_ENV`. | ||
|
||
If `APP_ENV` is set to **staging**, the app will use the variables from the staging file. | ||
|
||
<Card title="Development Stage" icon="code" iconType="light" color="#F96F5D"> | ||
In this stage, we work on making the app and adding new things. | ||
|
||
The app uses a file named `.env.development` which has special environment variables just for people who are building the app. | ||
</Card> | ||
|
||
<Card title="Testing Stage (Staging)" icon="bug" iconType="light" color="#ca8b04"> | ||
Here, we test the app to make sure it's ready to go live. | ||
|
||
We use the `.env.staging` file which has variables that help us test everything properly. | ||
</Card> | ||
|
||
<Card title="Live Stage (Production)" icon="industry-windows" iconType="light" color="#02C39A"> | ||
In the Production, the app is all done and people can use it. | ||
|
||
App uses a file named `.env.production` which has environment variables for the app when it's being used by everyone. | ||
</Card> | ||
|
||
### Environment Variables Schema Validation | ||
We use [Zod](https://zod.dev/) to check that our config is correct and in the right format. | ||
This is important to make sure the app works without any problems. | ||
|
||
This setup is found in the `src/config/index.ts` file. | ||
|
||
<Tip> | ||
To prefix any variables intended for client-side exposure with `NEXT_PUBLIC_`. | ||
</Tip> | ||
|
||
When we add new environment variables to .env files, we need to make sure they match what we set up in the `Zod` schema. | ||
|
||
## Adding a New Variable | ||
|
||
Here's how to add a new environment variable to the application: | ||
|
||
<Steps> | ||
<Step title="Identify the Environment Stages"> | ||
Determine the environment stages where the new variable will be used. | ||
|
||
Refer to the following table for file associations: | ||
|
||
| APP_ENV | File | | ||
|-------------|------------------| | ||
| development | .env.development | | ||
| staging | .env.staging | | ||
| production | .env.production | | ||
</Step> | ||
|
||
<Step title="Update `.env` Files"> | ||
Add the new variable to the respective `.env` files. For client-side variables, prefix them with `NEXT_PUBLIC_`. | ||
|
||
```bash | ||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_TYooMQauvdEDq54NiTphI7jx | ||
``` | ||
</Step> | ||
|
||
<Step title="Modify the Schema in `src/config/index.ts`"> | ||
Update the schema in the `config/index.ts` file to include the new variable. | ||
This step is crucial for schema validation. | ||
|
||
```typescript config/index.ts | ||
const schema = z.object({ | ||
... | ||
STRIPE_PUBLISHABLE_KEY: z.string(), | ||
}); | ||
``` | ||
</Step> | ||
|
||
<Step title="Update the Configuration Object"> | ||
Add the new variable to the configuration object within the same file (`config/index.ts`), ensuring it matches the schema. | ||
|
||
```typescript config/index.ts | ||
const processEnv = { | ||
... | ||
STRIPE_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY, | ||
}; | ||
``` | ||
</Step> | ||
</Steps> | ||
|
||
<Warning> | ||
All the environment variables we use in the front-end part of our app can be seen by everyone. | ||
So, don't put any secret stuff like passwords or private keys there. | ||
|
||
Keep those safe and only on the server side! 馃洝锔忊湪 | ||
</Warning> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
--- | ||
title: Forms | ||
--- | ||
|
||
## Overview | ||
|
||
We use the [react-hook-form](https://react-hook-form.com/) library along with [@hookform/resolvers](https://www.npmjs.com/package/@hookform/resolvers) to simplify the process of creating and managing forms. | ||
This setup allows for the quick development of robust forms. | ||
|
||
### Form Schema | ||
|
||
With [@hookform/resolvers](https://www.npmjs.com/package/@hookform/resolvers), you can validate your forms using a Zod schema. | ||
Just write the schema and use it as a resolver in the `useForm` hook. | ||
This approach not only validates your forms but also helps in creating perfectly typed forms by inferring types from the schema. | ||
|
||
Example of setting up a form with Zod schema: | ||
```tsx | ||
import { useForm } from 'react-hook-form'; | ||
import { zodResolver } from '@hookform/resolvers/zod'; | ||
import { z } from 'zod'; | ||
|
||
// Define your schema | ||
const schema = z.object({ | ||
firstName: z.string().min(1, 'Please enter First name').max(100), | ||
lastName: z.string().min(1, 'Please enter Last name').max(100), | ||
email: z.string().regex(EMAIL_REGEX, 'Email format is incorrect.'), | ||
}); | ||
|
||
// Infer type from schema | ||
type FormParams = z.infer<typeof schema>; | ||
|
||
const Page = () => { | ||
const { register, handleSubmit } = useForm<FormParams>({ | ||
resolver: zodResolver(schema), | ||
}); | ||
|
||
// Form handling code here | ||
}; | ||
``` | ||
|
||
### Error Handling | ||
For error handling in forms, we use the `handle-error.util.ts` utility function. | ||
This function parses error messages, sets them to the form fields' error states, or displays a global notification for general errors. | ||
|
||
Usage: | ||
```tsx | ||
import { useForm } from 'react-hook-form'; | ||
import { handleError } from 'utils'; | ||
|
||
const Page: NextPage = () => { | ||
const { setError } = useForm(); | ||
|
||
// Submit function | ||
const onSubmit = (data: SignUpParams) => signUp(data, { | ||
onError: (e) => handleError(e, setError), | ||
}); | ||
} | ||
``` | ||
|
||
#### Form usage | ||
|
||
Here is an example of how you can create a form: | ||
|
||
```tsx | ||
import { handleError } from 'utils'; | ||
import { accountApi } from 'resources/account'; | ||
import { useForm } from 'react-hook-form'; | ||
import { zodResolver } from '@hookform/resolvers/zod'; | ||
import { z } from 'zod'; | ||
|
||
// Define your schema | ||
const schema = z.object({ | ||
// Schema details here | ||
}); | ||
|
||
// Infer type from schema | ||
type SignUpParams = z.infer<typeof schema>; | ||
|
||
const Page: NextPage = () => { | ||
const { mutate: signUp, isLoading: isSignUpLoading } = accountApi.useSignUp(); | ||
const { | ||
register, | ||
handleSubmit, | ||
setError, | ||
formState: { errors }, | ||
} = useForm<SignUpParams>({ | ||
resolver: zodResolver(schema), | ||
}); | ||
|
||
// Submit function | ||
const onSubmit = (data: SignUpParams) => signUp(data, { | ||
onSuccess: (data) => { | ||
// Handle success response | ||
queryClient.setQueryData(['account'], data); | ||
}, | ||
onError: (e) => handleError(e, setError), | ||
}); | ||
|
||
// Form rendering | ||
return ( | ||
<form onSubmit={handleSubmit(onSubmit)}> | ||
<TextInput | ||
{...register('name')} | ||
error={errors.name?.message} | ||
/> | ||
|
||
<Button | ||
type="submit" | ||
loading={isSignUpLoading} | ||
> | ||
Submit | ||
</Button> | ||
</form> | ||
); | ||
}; | ||
``` | ||
|
||
By following these guidelines, you can effectively build and manage forms in our application, | ||
ensuring a smooth user experience and robust form handling. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.