Fix alll suggestions

This commit is contained in:
Vincent (Wen Yu) Ge
2024-01-11 03:07:11 +00:00
parent 4239f7d745
commit a786030446
8 changed files with 207 additions and 226 deletions

View File

@@ -27,6 +27,8 @@ npm install "@appwrite.io/pink"
Open `App.vue` and import the relevant style files.
```html
<!-- app.vue -->
<script setup>
import "@appwrite.io/pink";
@@ -39,6 +41,8 @@ import "@appwrite.io/pink-icons";
Then update `nuxt.config.ts` to disable SSR for now. SSR support is coming soon to Appwrite, for now, disable SSR.
```js
// nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
ssr: false,

View File

@@ -19,7 +19,7 @@ Head to the [Appwrite Console](https://cloud.appwrite.io/console).
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.
The **Hostname** should be `localhost`.
{% only_dark %}
![Add a platform](/images/docs/quick-starts/dark/add-platform.png)
@@ -56,18 +56,18 @@ Create a new file `appwrite.js` for the Appwrite related code.
Only one instance of the `Client()` class should be created per app.
Add the following code to it.
```js
// appwrite.js
```ts
// appwrite.ts
import { Client, Databases, Account } from "appwrite";
const url = import.meta.env.VITE_APPWRITE_ENDPOINT;
const project = import.meta.env.VITE_APPWRITE_PROJECT;
const url: string = import.meta.env.VITE_APPWRITE_ENDPOINT;
const project: string = import.meta.env.VITE_APPWRITE_PROJECT;
const client = new Client();
const client: Client = new Client();
client.setEndpoint(url).setProject(project);
export const account = new Account(client);
export const database = new Databases(client);
export const account: Account = new Account(client);
export const database: Databases = new Databases(client);
```

View File

@@ -23,45 +23,43 @@ The response from these interactions will be stored as references to get more in
In your `composable` directory, create the file `useUserSession.js` and add the following code.
Then you can call the `useUserSession()` function in the pages and components to use the functionality.
```js
// composable/useUserSession.js
```ts
// composable/useUserSession.ts
import { ID } from "appwrite";
import { ref } from "vue";
import { account } from "/appwrite";
import { account } from "../appwrite";
import { type Models } from 'appwrite';
const current = ref(null) // Reference to current user object
const isLoggedIn = ref(null) //Reference to check user status
export const useUserSession = () => {
const register = async (email, password) => {
await account.create(ID.unique(), email, password); // Register new user in Appwrite
await login(email, password); // Login registered user
};
const login = async (email, password) => {
const authUser = await account.createEmailSession(email, password); // Open user session in Appwrite
current.value = authUser; // Pass user data to current ref
isLoggedIn.value = true; // Set ref to true
navigateTo("/");
};
const current = ref<Models.Session | null>(null); // Reference to current user object
const logout = async () => {
await account.deleteSession("current"); // Delete Appwrite user session
current.value = null; // Clear current ref
isLoggedIn.value = false; // Set ref to false
navigateTo("/");
};
export const useUserSession = () => {
const register = async (email: string, password: string): Promise<void> => {
await account.create(ID.unique(), email, password); // Register new user in Appwrite
await login(email, password); // Login registered user
};
return {
current,
isLoggedIn,
login,
logout,
register,
};
const login = async (email: string, password: string): Promise<void> => {
const authUser = await account.createEmailSession(email, password); // Open user session in Appwrite
current.value = authUser; // Pass user data to current ref
navigateTo("/");
};
const logout = async (): Promise<void> => {
await account.deleteSession("current"); // Delete Appwrite user session
current.value = null; // Clear current ref
navigateTo("/");
};
return {
current,
login,
logout,
register,
};
};
```
# Login page {% #login-page %}
@@ -75,7 +73,7 @@ In step 5 we will add a loginbutton that will redirect us to this page.
Add the following code to build the form.
```vue
// pages/login.vue
<!-- pages/login.vue -->
<template>
<div class="u-max-width-650" style="margin: 0 auto;">
@@ -140,35 +138,27 @@ Add the following code to build the form.
</div>
</template>
<script>
export default {
setup() {
// Access user composable functions
const user = useUserSession();
<script setup>
import { ref } from 'vue';
// Store user input
const userData = {
email: "",
password: "",
};
// Access user composable functions
const user = useUserSession();
// Login user event handler
const handleLogin = async () => {
await user.login(userData.email, userData.password);
};
// Store user input
const userData = ref({
email: "",
password: "",
});
// Register user event handler
const handleRegistration = async () => {
await user.register(userData.email, userData.password);
};
// Login user event handler
const handleLogin = async () => {
await user.login(userData.value.email, userData.value.password);
};
return {
handleLogin,
handleRegistration,
userData,
};
},
};
// Register user event handler
const handleRegistration = async () => {
await user.register(userData.value.email, userData.value.password);
};
</script>
```

View File

@@ -14,7 +14,7 @@ We will also put the user's e-mail address next to the logout button.
From the `components` directory, create the file `navbar.vue` and add the code below.
```vue
// components/navbar.vue
<!-- components/navbar.vue -->
<template>
<div>
@@ -24,9 +24,9 @@ From the `components` directory, create the file `navbar.vue` and add the code b
<!-- Email and logout button if logged in user -->
<div
class="main-header-end u-margin-inline-end-16"
v-if="user.isLoggedIn.value === true"
>
<p>{{ user.current.value.providerUid }}</p>
v-if="user.current.value"
>
<p>{{ user.current.providerUid }}</p>
<button class="button" type="button" @click="user.logout()">
Logout
</button>
@@ -39,32 +39,31 @@ From the `components` directory, create the file `navbar.vue` and add the code b
</div>
</template>
<script>
export default {
setup() {
const user = useUserSession(); // Access user composable function
return {
user,
};
},
};
<script setup>
// Access user composable function
const user = useUserSession();
</script>
```
Open `app.vue` from the root directory and add the navigation bar.
```vue
// app.vue
<template>
<NuxtLayout>
<!-- Add navbar -->
<Navbar />
<NuxtPage />
</NuxtLayout>
<template>
<!-- app.vue -->
<script setup>
import "@appwrite.io/pink";
import "@appwrite.io/pink-icons";
</script>
<template>
<div>
<NuxtLayout>
<!-- Add navbar -->
<Navbar />
<NuxtPage />
</NuxtLayout>
</div>
</template>
```
Have a look in the browser at both the main page and the login page to test the new functionality.

View File

@@ -5,23 +5,32 @@ description: Add databases and queries for ideas in your Nuxt project.
step: 6
---
In Appwrite, data is stored as a collection of documents. Create a collection in the [Appwrite Console](https://cloud.appwrite.io/) to store the ideas.
In Appwrite, data is stored as a collection of documents.
Create a new database and collection in the [Appwrite Console](https://cloud.appwrite.io/) to store the ideas.
{% only_dark %}
![Create project screen](/images/docs/tutorials/dark/idea-tracker-collection.png)
![Create collection screen](/images/docs/tutorials/dark/idea-tracker-collection.png)
{% /only_dark %}
{% only_light %}
![Create project screen](/images/docs/tutorials/idea-tracker-collection.png)
![Create collection screen](/images/docs/tutorials/idea-tracker-collection.png)
{% /only_light %}
Create a new collection with the following attributes:
| Field | Type | Required |
|-------------|--------|----------|
| userId | String | Yes |
| title | String | Yes |
| description | String | No |
| Field | Type | Required | Size |
|-------------|--------|----------|----------|
| userId | String | Yes | 200 |
| title | String | Yes | 200 |
| description | String | No | 500 |
Change the collection's permissions in the settings to give access.
{% only_dark %}
![Collection permissions screen](/images/docs/tutorials/dark/idea-tracker-permissions.png)
{% /only_dark %}
{% only_light %}
![Collection permissions screen](/images/docs/tutorials/idea-tracker-permissions.png)
{% /only_light %}
Navigate to the **Settings** tab of your collection, add the role **Any** and check the **Read** box.
Next, add a **Users** role and give them access to **Create**, **Update** and **Delete** by checking those boxes.
@@ -44,55 +53,60 @@ We will add a new composable, `useIdeas`, to handle this functionality.
Create a new file in the composables directory, `useIdeas.js` and add the following code.
```js
// composables/useIdeas.js
// composables/useIdeas.ts
import { ID, Query } from "appwrite";
import { ID, Query, Models} from "appwrite";
import { database } from "~/appwrite";
import { ref } from "vue";
const ideasDatabaseId = import.meta.env.VITE_DATABASE_ID;
const ideasCollectionId = import.meta.env.VITE_COLLECTION_ID;
const ideasDatabaseId: string = import.meta.env.VITE_DATABASE_ID;
const ideasCollectionId: string = import.meta.env.VITE_COLLECTION_ID;
const current = ref(null); //Reference for the fetched data
interface Idea extends Models.Document{
title: string;
description: string;
userId: string;
}
const current = ref<Idea[] | null>(null); // Reference for the fetched data
export const useIdeas = () => {
// Fetch the 10 most recent ideas from the database
// Add the list to the current reference object
const fetch = async () => {
const response = await database.listDocuments(
ideasDatabaseId,
ideasCollectionId,
[Query.orderDesc("$createdAt"), Query.limit(10)]
);
current.value = response.documents;
};
// Fetch the 10 most recent ideas from the database
// Add the list to the current reference object
const fetch = async (): Promise<void> => {
const response = await database.listDocuments(
ideasDatabaseId,
ideasCollectionId,
[Query.orderDesc("$createdAt"), Query.limit(10)]
);
current.value = response.documents as Idea[];
};
// Add new idea to the database,
// Change the value of the current object
const add = async (idea) => {
const response = await database.createDocument(
ideasDatabaseId,
ideasCollectionId,
ID.unique(),
idea
);
current.value = [response, ...current.value].slice(0, 10);
};
// Add new idea to the database,
// Change the value of the current object
const add = async (idea: Idea): Promise<void> => {
const response = await database.createDocument(
ideasDatabaseId,
ideasCollectionId,
ID.unique(),
idea
);
current.value = [response, ...current.value as Idea[]].slice(0, 10) as Idea[];
};
const remove = async (id) => {
await database.deleteDocument(ideasDatabaseId, ideasCollectionId, id);
await fetch(); //Refetch ideas to ensure we have 10 items
};
const remove = async (id: string): Promise<void> => {
await database.deleteDocument(ideasDatabaseId, ideasCollectionId, id);
await fetch(); // Refetch ideas to ensure we have 10 items
};
return {
add,
current,
fetch,
remove,
};
return {
add,
current,
fetch,
remove,
};
};
```
Now we can call the `useIdeas()` composable from the home page.

View File

@@ -17,7 +17,7 @@ The form need a text field for filling in the title, a textarea for the descript
From the `components` directory, add the file `ÌdeasForm.vue` and add the following code.
```vue
// components/IdeasForm.vue
<!-- components/ideasForm.vue -->
<template>
<div>
@@ -31,7 +31,7 @@ From the `components` directory, add the file `ÌdeasForm.vue` and add the follo
<input
type="text"
placeholder="Title"
v-model="addIdeaData.title.value"
name="title"
/>
</li>
<!-- Description input field -->
@@ -39,7 +39,7 @@ From the `components` directory, add the file `ÌdeasForm.vue` and add the follo
<label class="label">Description</label>
<textarea
placeholder="Description"
v-model="addIdeaData.description.value"
name="description"
/>
</li>
<button class="button" aria-label="Submit idea" type="submit">
@@ -51,39 +51,26 @@ From the `components` directory, add the file `ÌdeasForm.vue` and add the follo
</div>
</template>
<script>
export default {
setup() {
const ideas = useIdeas();
const user = useUserSession();
<script setup>
const ideas = useIdeas();
const user = useUserSession();
// Store input field values
const addIdeaData = {
title: ref(""),
description: ref(""),
};
const handleAddIdea = async (event) => {
const form = event.target;
const formData = new FormData(form);
const handleAddIdea = async () => {
// Extract the values from the refs and add userId
const postIdeaData = {
userId: user.current.value.userId,
title: addIdeaData.title.value,
description: addIdeaData.title.value,
};
await ideas.add(postIdeaData);
addIdeaData.title.value = ""; // Clear the title textfield
addIdeaData.description.value = ""; // Clear the description textarea
};
return {
addIdeaData,
handleAddIdea,
ideas,
};
},
// Extract the values from the FormData object and add userId
const postIdeaData = {
userId: user.current.value.userId,
title: formData.get('title'),
description: formData.get('description'),
};
</script>
await ideas.add(postIdeaData);
form.reset(); // Clear the form
};
</script>
```
Next, add the component to the page `pages/index.vue` by auto-importing it in the `<template>`tag.
@@ -97,12 +84,12 @@ Add the following code to the `index.vue`page to conditionally render the form a
Overwrite the contents of `pages/index.vue` with the following code.
```vue
// pages/index.vue
<!-- pages/index.vue -->
<template>
<div class="u-max-width-650" style="margin: 0 auto;">
<!-- Idea form component for logged in users -->
<section v-if="user.isLoggedIn.value === true" class="card u-margin-32">
<section v-if="user.current.value" class="card u-margin-32">
<IdeasForm />
</section>
@@ -114,27 +101,20 @@ Overwrite the contents of `pages/index.vue` with the following code.
</p>
</div>
</section>
<IdeasList />
</div>
</template>
<script>
export default {
setup() {
const user = useUserSession();
return {
user,
};
},
};
<script setup>
// Access user composable function
const user = useUserSession();
</script>
<style>
article.box {
background-color: hsl(var(--color-neutral-0));
}
article.box {
background-color: hsl(var(--color-neutral-0));
}
</style>
````
# Ideas list {% #ideas-list %}
@@ -150,62 +130,56 @@ We help them do that by adding a delete button in the top right corner of the id
Add the file `IdeasList`from the `componenents` directory and insert the following code:
```vue
// componenents/IdeasList.vue
<!-- componenents/ideasList.vue -->
<template>
<section class="u-margin-32">
<article class="card">
<h4 class="heading-level-4">Latest Ideas</h4>
<ul class="u-margin-block-start-8">
<li v-for="idea in ideas.current.value">
<div class="box">
<h5 class="heading-level-6">{{ idea.title }}</h5>
<p class="body-text-2">{{ idea.description }}</p>
<div
class="u-position-absolute u-inset-inline-end-8 u-inset-block-start-8"
>
<button
class="button is-small is-text is-only-icon"
aria-label="Remove item"
v-if="
user.current.value &&
idea.userId === user.current.value.userId
"
type="button"
@click="ideas.remove(idea.$id)"
>
<span class="icon-document-remove" aria-hidden="true" />
</button>
</div>
</div>
</li>
</ul>
</article>
</section>
<section class="u-margin-32">
<article class="card">
<h4 class="heading-level-4">Latest Ideas</h4>
<ul class="u-margin-block-start-8">
<template v-if="ideas.current.value && ideas.current.value.length">
<li v-for="idea in ideas.current.value">
<div class="box">
<h5 class="heading-level-6">{{ idea.title }}</h5>
<p class="body-text-2">{{ idea.description }}</p>
<div class="u-position-absolute u-inset-inline-end-8 u-inset-block-start-8">
<button class="button is-small is-text is-only-icon" aria-label="Remove item" v-if="user.current.value &&
idea.userId === user.current.value.userId
" type="button" @click="ideas.remove(idea.$id)">
<span class="icon-document-remove" aria-hidden="true" />
</button>
</div>
</div>
</li>
</template>
<template v-else>
<p>No ideas yet.</p>
</template>
</ul>
</article>
</section>
</template>
<script>
export default {
setup() {
const ideas = useIdeas();
const user = useUserSession();
<script setup>
import { onMounted } from 'vue';
// Get ideas on page load
onMounted(() => {
ideas.fetch();
});
return { ideas, user };
},
};
const ideas = useIdeas();
const user = useUserSession();
console.log(ideas.current.value);
// Get ideas on page load
onMounted(() => {
ideas.fetch();
});
</script>
```
Return to the file `pages/index.vue` once more to import the list of ideas.
This component should be visible to all users, so no conditional rendering neeeds to be handled.
```vue
// pages/index.vue
<!-- pages/index.vue -->
<template>
<div class="u-max-width-650" style="margin: 0 auto;">