Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(docs): add pagination tutorial and related files #196

11 changes: 11 additions & 0 deletions src/routes/docs/tutorials/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@
</p>
</a>
</li>
<li class="is-mobile-col-span-2">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's leave it out, we'lll update this page later with all of the new tutorials

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I removed it. So now you travel to it by the URL?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@timDeHof We will add it somewhere else, but we need to figure out how we'll do it. Dw about it, we'll handle it on our side :D

<a href="/docs/tutorials/pagination" class="aw-card is-normal">
<header class="u-flex u-cross-baseline u-gap-4">
<span class="icon-react aw-u-font-size-24" aria-hidden="true" />
<h4 class="aw-sub-body-500 aw-u-color-text-primary">Pagination</h4>
</header>
<p class="aw-sub-body-400 u-margin-block-start-4">
Learn Appwrite Pagination and more with React.
</p>
</a>
</li>
<li class="is-mobile-col-span-2">
<a href="/docs/tutorials/vue" class="aw-card is-normal">
<header class="u-flex u-cross-baseline u-gap-4">
Expand Down
10 changes: 10 additions & 0 deletions src/routes/docs/tutorials/pagination/+layout.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';

export let data;
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>

<slot />
11 changes: 11 additions & 0 deletions src/routes/docs/tutorials/pagination/+layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { LayoutLoad } from './$types';

export const load: LayoutLoad = ({ url }) => {
const tutorials = import.meta.glob('./**/*.markdoc', {
eager: true
});
return {
tutorials,
pathname: url.pathname
};
};
6 changes: 6 additions & 0 deletions src/routes/docs/tutorials/pagination/+page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { redirect } from '@sveltejs/kit';
import type { PageLoad } from './$types';

export const load: PageLoad = async () => {
throw redirect(303, '/docs/tutorials/pagination/step-1');
};
30 changes: 30 additions & 0 deletions src/routes/docs/tutorials/pagination/step-1/+page.markdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
layout: tutorial
title: Build a todos app with react
description: Learn about using pagination in a react app using appwrite backend.
step: 1
difficulty: beginner
readtime: 10
---

**Todos App**: An app to track all your todos that you'll need to get done. In this tutorial, you will build the todos app with appwrite and react.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Focus on the end goal, title and this part should focus on general pagination with Appwrite + react, not the app.


# Overview {% #overview %}
**What is pagination in web development and why is it important?**
Pagination is a technique used in web development to divide a large dataset into smaller manageable chunks. This practice enhances user experience by facilitating easy navigation through the dataset, making the data consumption less overwhelming. It's a common feature in data-rich applications, such as search engines and task management systems, offering benefits like reduced load times, easier data management, and a cleaner, more organized user interface.

In this tutorial, while building the Todos app using Appwrite and React, you will use some pagination methods that Appwrite offers to manage the display of tasks efficiently, ensuring a user-friendly interface that allows users to navigate through their tasks effortlessly.

# Concepts {% #concepts %}

This tutorial will introduce the following concepts:

1. Setting up your first project with Appwrite and React.
2. Understanding databases and collections in Appwrite.
3. Crafting queries and implementing pagination to manage data efficiently.
4. Utilizing storage solution for your Todo App.

# Prerequisites {% #prerequisites %}

1. Basic knowledge of JavaScript and React.
2. Have the latest version of [Node.js](https://nodejs.org/en) and [NPM](https://www.npmjs.com/) installed on your computer
18 changes: 18 additions & 0 deletions src/routes/docs/tutorials/pagination/step-10/+page.markdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
layout: tutorial
title: Next steps
description: Run your react app built with appwrite
step: 10
---

# Test your project {% #test-project %}
Run your project

```sh
npm run dev -- --open --port 3000
```

open [http://localhost:3000](http://localhost:3000) in your browser.

# Further resources {% #further-resources %}
Read more about it in the [pagination docs here](/docs/products/databases/pagination).
28 changes: 28 additions & 0 deletions src/routes/docs/tutorials/pagination/step-2/+page.markdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
layout: tutorial
title: Create app
description: Create a react app project and integrate it with appwrite.
step: 2
---

# Create React project {% #create-react-project %}

Create a react app with the `npm create` command from [vite](https://vitejs.dev/).

```sh
npm create vite@latest tasks-app -- --template react && cd tasks-app
```

# Add dependencies {% #add-dependencies %}

Install the JavaScript Appwrite Web SDK.

```sh
npm install appwrite
```

You can start the development server to watch your app update in the browser as you make changes.

```sh
npm run dev -- --open --port 3000
```
60 changes: 60 additions & 0 deletions src/routes/docs/tutorials/pagination/step-3/+page.markdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
layout: tutorial
title: Set up appwrite
description: Import and initialize appwrite for your react application.
step: 3
---
# Create project {% #create-project %}

Head to the [Appwrite Console](https://cloud.appwrite.io/console).

{% only_dark %}
![Create project screen](/images/docs/quick-starts/dark/create-project.png)
{% /only_dark %}
{% only_light %}
![Create project screen](/images/docs/quick-starts/create-project.png)
{% /only_light %}

If this is your first time using Appwrite, create an account and create your first project.

Then, under **Add a platform**, add a **Web app**. The **Hostname** should be localhost.

{% only_dark %}
![Add a platform](/images/docs/quick-starts/dark/add-platform.png)
{% /only_dark %}
{% only_light %}
![Add a platform](/images/docs/quick-starts/add-platform.png)
{% /only_light %}

You can skip optional steps.

# Initialize Appwrite SDK {% #init-sdk %}

To use Appwrite in our react app, we'll need to find our project ID. Find your project's ID in the **Settings** page.

{% only_dark %}
![Project settings screen](/images/docs/quick-starts/dark/project-id.png)
{% /only_dark %}
{% only_light %}
![Project settings screen](/images/docs/quick-starts/project-id.png)
{% /only_light %}

Create a new file `src/lib/appwrite.js` to hold our Appwrite related code.

{% info title="One Client() instance per an app" %}
Only one instance of the `Client()` class should be created per an app.
{% /info %}

Add the following code to it, replacing `<YOUR_PROJECT_ID>` with your project ID.

```js
import { Client, Databases } from "appwrite";

const client = new Client();

client
.setEndpoint("https://cloud.appwrite.io/v1")
.setProject("[YOUR_PROJECT_ID]"); // Replace with your project ID

export const databases = new Databases(client);
```
23 changes: 23 additions & 0 deletions src/routes/docs/tutorials/pagination/step-4/+page.markdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
layout: tutorial
title: Add database
description: Add a database to your react application using appwrite web SDK.
step: 4
---

# Create collection {% #create-collection %}
In Appwrite, a collection of documents stores the data. You can think of it like a filing cabinet. Create a collection in the [Appwrite Console](https://cloud.appwrite.io/) to store our ideas.

{% only_dark %}
![Create project screen](/images/docs/tutorials/dark/idea-tracker-collection.png)
{% /only_dark %}
{% only_light %}
![Create project screen](/images/docs/tutorials/idea-tracker-collection.png)
{% /only_light %}

Create a new collection with the following attributes:
| Field | Type | Required |
|-------------|--------|----------|
| taskId | Number | Yes |
| title | String | Yes |
| completed | boolean | Yes |
64 changes: 64 additions & 0 deletions src/routes/docs/tutorials/pagination/step-5/+page.markdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
layout: tutorial
title: Environment setup
description: Setup the environment for seeding the database
step: 5
---

# Seeding the collection {% #seeding-the-collection %}
Now we need to create a script that will create documents from a setup.json file.Create a new file `./db/setup.js` in the root of the project directory to hold our related code.

Copy link
Author

@timDeHof timDeHof Oct 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Create environment configuration file {% #create-env-file %}
Now, let's set up the environment configuration for our project.
Follow these steps to create a `.env` file in the root directory of the project.
1. Navigate to the root of the project using your terminal or file explorer.
2. Create a new file named `.env` (make sure that there is no file extension like `.txt`).
3. Open the `.env` file with your preferred text editor.
4. Add the necessary environment variables and their values for your respective project, database, and collection IDs in the format `KEY=VALUE`. Each variable should be on a new line.
{% code %}
APPWRITE_PROJECT_ID=[YOUR_PROJECT_ID]
APPWRITE_DATABASE_ID=[YOUR_DATABASE_ID]
APPWRITE_COLLECTION_ID=[YOUR_COLLECTION_ID]
{% /code %}
4. Save the file.
This `.env` file will now act as the central place for your project's environment-specific configurations. Make sure not to sure this file publicly, especially if it contains sensitive information like API keys or database credentials.

Add the following code to it, replacing `[YOUR_DATABASE_ID]` and `[YOUR_COLLECTION_ID]` with your project ID and collection ID.

```js
import { databases } from "../src/lib/appwrite.js";
import { ID } from "appwrite";

export const TODOS_DATABASE_ID = "[YOUR_DATABASE_ID]";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's life all these into a .env file

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const TODOS_DATABASE_ID = "[YOUR_DATABASE_ID]";

export const TODOS_COLLECTION_ID = "[YOUR_COLLECTION_ID]";
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const TODOS_COLLECTION_ID = "[YOUR_COLLECTION_ID]";

const dataURL = "https://jsonplaceholder.typicode.com/users/1/todos";

async function seedCollection() {
const response = await fetch(dataURL);
const data = await response.json();
console.log("Starting to seed collection...");
try {
for (let i = 0; i < data.length; i++) {
const item = data[i];
let promise = await databases.createDocument(
TODOS_DATABASE_ID,
TODOS_COLLECTION_ID,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since the app is created using Vite, these env variables should be like this:

Suggested change
TODOS_DATABASE_ID,
TODOS_COLLECTION_ID,
import.meta.env.APPWRITE_DATABASE_ID,
import.meta.env.APPWRITE_COLLECTION_ID,

we can tell them to write them like this so the env variables will not be exposed to the client.

ID.unique(),
{
taskId: item.id,
title: item.title,
completed: item.completed,
},
);
console.log(`${promise.title} has been added to the collection`);
}
console.log("Seeding collection done!");
} catch (error) {
console.error(error);
}
}

seedCollection();

```

# Add seed script to package.json {% #add-seed-script %}

Open the project's package.json file and go to the `scripts` and add the following script to the list.

```sh
"db:seed": "node ./db/setup.js",
```

You can now open a terminal and run the following command.

```sh
npm run db:seed
```

You should see the command running in the terminal.When it's done check the collection to see it populated with the data.
106 changes: 106 additions & 0 deletions src/routes/docs/tutorials/pagination/step-6/+page.markdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
layout: tutorial
title: Adding tasks
description: Add tasks to your react application using appwrite
step: 6
---
# Add tasks context {% #add-tasks-context %}

Now we need to display our Todos to the page. In React, we can use [context](https://reactjs.org/docs/context.html) to share data between components. We'll use context and create a simple custom hook to manage out tasks.

Create a new file `src/lib/context/tasks.jsx` and add the following code to it.

```js
import { createContext, useContext, useEffect, useState } from "react";
import { databases } from "../appwrite";
import { Query } from "appwrite";

export const TODOS_DATABASE_ID = "[YOUR_DATABASE_ID]";
export const TODOS_COLLECTION_ID = "[YOUR_COLLECTION_ID]";

const TasksContext = createContext();

export function useTasks() {
return useContext(TasksContext);
}

export function TasksProvider(props) {
const [tasks, setTasks] = useState([]);

async function init() {
try {
const response = await databases.listDocuments(
TODOS_DATABASE_ID,
TODOS_COLLECTION_ID,
[Query.orderDesc("$createdAt"), Query.limit(4)],
);
setTasks(response.documents);
} catch (error) {
console.log(error);
}
}

useEffect(() => {
init();
}, []);

return (
<TasksContext.Provider value={{ current: tasks }}>
{props.children}
</TasksContext.Provider>
);
}
```
we will add more functionality to the context later

## Basic Routing {% #basic-routing %}

First, wrap the `main` element with the `TasksProvider` component.

Update `src/App.jsx` to the following code.

```js
import { TasksProvider } from "./lib/context/tasks";
import { Tasks } from "./pages/Tasks";

function App() {
return (
<div>
<TasksProvider>
<Tasks />
</TasksProvider>
</div>
);
}

export default App;
```

### Tasks page {% #tasks-page %}

We are now able to create the Tasks page that will show users a list of tasks that is a small chuck of the large dataset.

Create a new file `src/pages/Tasks.jsx` and add the following stub code to it.

```js
import { useTasks } from "../lib/context/tasks";
export function Tasks() {
const tasks = useTasks();
return (
<section>
<h2>Todos</h2>
<ul>
{tasks.current.map((task) => (
<li key={`task-${task.taskId}`}>
<span>{`Task-${task.taskId}: `}</span>
<input type='checkbox' id={task.taskId} name={task.title} />
<label htmlFor={task.title}>{task.title}</label>
</li>
))}
</ul>
</section>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wild suggestion, is it possible to take out all this complexity, focus on pagination, and just have pagination and UI all in app.jsx?

Proper practice? No
More focused on Pagination? Yes

Copy link
Author

@timDeHof timDeHof Oct 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and would this be the way you would want the pagination methods be structured too. so the user can just overwrite the app.jsx with the method they want to explore more.

);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or another idea, if we have this scaffolding, why don't we have each pagination method be in it's own page so someone could look through all 3?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean having one page with three sections on it?
For example, each method would have a section on it like this # Basic offset pagination {% #basic-offset-pagination %} ....

```

In the next two steps, we will explore how to use offset or cursor pagination in our application.
Loading