# To-Do app

StartupJs already has a full-fledged UI component library built in **@startupjs/ui**. You have the opportunity either to use ready-made components or to create your own (based on ready-made components as well).


## Your first complete application with StartupJS

In this section of the tutorial, we will create a complete **TODO List** application step by step. Let us see how to add ready-made components to the page, style them, create a query in the database, fetch documents from the database and display them on the page. We will learn how to add the documents to the database and how to delete them from the database as well. And all of this will be reactive!

## Create a new project

Follow the instructions from [Quickstart](./Quickstart) to create a new project.

Go to the file `main/pages/PHome/index.jsx` and replace the whole `return` block there with just an empty block:

```js
return (
  <>
    {/* Content goes here */}
  </>
)
```

Going forward you will be doing edits in this file.

## Key components of the wrapper

In usual HTML we mostly use `div` tag to wrap the components into the unified group. In our components library we use the `Div` component for this purpose.

Since **StartupJS** is based on React Native, all the elements by default have some differences from the regular web. Among them:
1.	All elements initially have a css property `display: flex`
2.	All elements `position: relative`
3.	Default alignment of items in a flex container `flex-direction: column`

> You do not have to override the `flex-direction` property of the `Div` component to `flex-direction: row`, just use the ready-made `Row` component.


## Adding the first component

Let us add the first component to the page.
This will be the `Content` component from our UI library. `Content` component is used as the parent container for elements.

First, you need to import it into our file:

```js
import { Content } from '@startupjs/ui'
```

Then add the following JSX to be returned from your component:

```js
return (
  <>
    <Content></Content>
  </>
)
```

> Please note that the UI-component are capitalized to distinguish them from ordinary HTML tags
> span !== Span

Then, add a title with the name of our future application to the page. For the first level heading, use the `H1` component.

First, add it to the import:
```js
import { Content, H1 } from '@startupjs/ui'
```

Then add the header that was previously added to `Content`

```js
<Content>
  <H1>TODO List</H1>
</Content>
```

You will see the first level heading on the right side in the preview.


### Content Component Attributes

As you can see, Content has centered our header with left and right paddings, but it is clear that the top and bottom paddings are missing.

Let us look at the [documentation for the Content component](/docs/components/Content#vertical-paddings). We see that to add padding on all four sides, we need to add the `padding` prop to component.

Let us do so:

```js
<Content padding>
  <H1>TODO List</H1>
</Content>
```

Now there are paddings on all sides.

---

## Adding the Styles

As with regular HTML, you can use the `style` attribute on a component. Don't be afraid to use the same classes in your components, because all components are compiled independently using `css-modules`.

Let us add some styling to the `<H1>` element:

```js
<H1 style={{color: 'purple'}}>TODO List</H1>
```

This method is totally acceptable, but far from preferable.
Startupjs uses classes to style components, like [classnames](https://github.com/JedWatson/classnames).

Remove the `style` prop from the `H1` component and add the `styleName` prop instead:

```js
<H1 styleName={'title'}>TODO List</H1>
```
Thus, you gave the title H1 a `title` class and now in the css file `index.styl` (in the same folder as the `.jsx` file) you can refer to it by the `.title` selector, adding the necessary styles to it

```css
.title {
  color: purple;
}
```

> Among other things, `styleName` accepts not only a string, but also an array of values or object. For example:

```js
 const variant = 'primary'
 const color = 'warning'
 const disabled = true
 const active = false

 return (
   <Button styleName={[variant, color, { disabled, active }]} />
  )
```

## Adding the card component

Next, we need to draw a card component within which we will display one of our tasks.

Let us add `Card` to our import:

```jsx
import { Content, H1, Card } from '@startupjs/ui'
```

And place it beneath our heading, adding some random text to it:

```jsx
<Content padding>
  <H1>TODO List</H1>
  <Card>Learn StartupJS</Card>
</Content>
```
As you can see, this code causes an error:

> Unexpected text node: Learn StartupJS. A text node cannot be a child of a `<View>`.

This is because in React Native you cannot simply put text in components without wrapping it in a text component `<Text>` or the derivatives of it (eg `<H1>`). In this case, we will use the `Span` component, which allows to display text (analogous to `Text` from React Native).

```js
<Content padding>
  <H1>TODO List</H1>
  <Card>
    <Span>Learn StartupJS</Span>
  </Card>
</Content>
```

> Don’t forget to import `Span` from `@startupjs/ui`

## Displaying a list with TODO

Now that our database has a collection of `todos` with several ready-made tasks, let's fetch them from the database and display them on the page, wrapping each `todo` in a `Card` component using `Array.map()`

First, let us fetch the data from the database. To do this, we can use a custom hook `useQuery`, which we will discuss in detail in the advanced course of this tutorial. In short, it accepts a string with the name of the collection as the first argument, and a [mongodb like request](https://derbyjs.com/docs/derby-0.10/models/queries#mongodb-query-format) as an object as the second argument. It returns an array of two elements, the first is getter, and the second is setter. Let us add the following code to the beginning:

```jsx
const [todos, $todos] = useQuery('todos', {})
```

> An empty object `{}` allows us to return all documents from the collection.

We now have a variable `todos`, which stores an array of `todo` in the format:

```js
[{
  id: 1,
  title: 'Learn StartupJS',
  completed: false
}]
```

It remains only to display them on the page:

```jsx
const [todos, $todos] = useQuery('todos', {})

<Content padding>
  <H1>TODO List</H1>
    {todos.map(todo => (
      <Card key={todo.id}>
        <Span>{todo.title}</Span>
      </Card>
    ))}
</Content>
```

## Task management

Now we have a list of tasks displayed on the page. Next, you need to add the possibility to mark tasks as completed and the ability to delete them. For this, we need two new frequently used components: `Checkbox` and `Button`, let’s add them:

> I think you do not need to be reminded about importing

```jsx
const [todos, $todos] = useQuery('todos', {})

<Content padding>
  <H1>TODO List</H1>
    {todos.map(todo => (
      <Card key={todo.id}>
        <Span>{todo.title}</Span>
        <Checkbox />
        <Button />
      </Card>
    ))}
</Content>
```

As you can see, the checkbox and the button have appeared on the page, but they are located below the text of our task. Let us use the `Row` wrapper to place the text, checkbox and button in one row and unite the checkbox with the button into one wrapper by analogy:

```jsx
const [todos, $todos] = useQuery('todos', {})

<Content padding>
  <H1>TODO List</H1>
    {todos.map(todo => (
      <Card key={todo.id}>
        <Row align='between' vAlign='center'>
          <Span>{todo.title}</Span>
            <Row align='between' vAlign='center'>
              <Checkbox />
              <Button />
            </Row>
        </Row>
      </Card>
    ))}
</Content>
```

> `align='between' vAlign='center'` – props from [Row component documentation](/docs/components/Row), analogue `justify-content: space-between; align-items: center;`

## Component state management

Just as in a regular JS and React application, you can manage the state of your components, yet in **StartupJS/UI** you will have more convenient tools for this (API).

Let us start with the previously added `Checkbox` component.

According to the [documentation](/docs/forms/Checkbox), the checkbox has two main props: `value` and `onChange`.

`value` receives the current boolean value of the checkbox, which we can take right from the array received from the database.
`onChange` takes a callback function that is called when the checkbox is clicked upon, and returns its new value.

Let us create a callback function and add these two props to our checkbox.
> In the example below, `$todos` is the model for the (todos) collection, but it isn’t necessary for now to go into detail. We will study what a model is and how to work with it later in our course.

The callback takes the `id` of our task and its new value, and then sets the value to the database respectively to `id` in the `completed` field.

In `value`, we pass the `completed` value right away.


```jsx
const changeStatus = (todoId, value) => $todos.at(todoId).set('completed', value)

<Checkbox
  value={todo.completed}
  onChange={value => changeStatus(todo.id, value)}
/>
```

Now, clicking on the checkbox alters the `completed` value of the task in our database.

## Deleting tasks

We have finished our checkbox, and now we just have to make a button for deleting the task from the database.

Add an icon to our button, paint it red and make it small.

The icon must be imported in advance:

```jsx
import { faTimes } from '@fortawesome/free-solid-svg-icons'

<Button
  icon={faTimes}
  iconColor='error'
  size='s'
/>
```

> You can learn more about these and other props of the `Button` component in the [documentation](/docs/components/Button)

`Button` also has an `onPress` prop. It takes a callback function that is called upon whenever the button is clicked. Here we need to write a function that will remove a certain task from the database.

```js
const removeTodo = todoId => $todos.at(todoId).del()
```

> We are looking for a task with the required ID in the $todos model, and then we call its delete method.

```jsx
<Button
  icon={faTimes}
  iconColor='error'
  size='s'
  onPress={() => removeTodo(todo.id)}
/>
```

The delete task button is now fully functional.

## Form for adding a new task

Our TODO List is almost completed; for full-fledged application we are only missing the feature of adding new tasks.
For this, we need one new component `TextInput` and the one that we already know – `Button`.

In `TextInput` we will enter the text of the new task, while `Button` will add the task to the database.

Let us add two components to our file:

```jsx
const [todos, $todos] = useQuery('todos', {})

<Content padding>
  <H1>TODO List</H1>
    {todos.map(todo => (
      <Card key={todo.id}>
        <Row align='between' vAlign='center'>
          <Span>{todo.title}</Span>
            <Row align='between' vAlign='center'>
              <Checkbox
                value={todo.completed}
                onChange={value => changeStatus(todo.id, value)}
              />
              <Button
                icon={faTimes}
                iconColor='error'
                size='s'
                onPress={() => removeTodo(todo.id)}
              />
            </Row>
        </Row>
      </Card>
    ))}
    <Row align='between' vAlign='center'>
      <TextInput />
      <Button>Add</Button>
    </Row>
</Content>
```

## Text input box

According to the [documentation](/docs/forms/TextInput), we need two props from TextInput for our purposes: `value` and `onChangeText`.

To work with it, we need to create a state with an empty initial line:

```js
const [inputValue, setInputValue] = useState('')
```

`value` props will accept a state getter `inputValue`.
The `onChangeText` prop accepts a callback function that is called when the content of the input is altered. In our case, it will simply accept a state setter (`setInputValue`).

```js
const [inputValue, setInputValue] = useState('')

<TextInput
  value={inputValue}
  onChangeText={setInputValue}
/>
```

Thus, we have connected `TextInput` to the local state.


## “Add new task” button

As we already know from the previous lessons, the `Button` component accepts an `onPress` prop as a callback function. In this case, we need to add the input submission handler to the button. In short, the function should take `inputValue`, check whether it is empty: if it is - exit the function, otherwise - add a new task to `$todos` and “clear” the input.

This is how it will look:

```jsx
const [inputValue, setInputValue] = useState('')

const handleSubmit = async () => {
  if (!inputValue) return
  await $todos.add({
       title: inputValue,
       completed: false
  })
  setInputValue('')
}

<Button onPress={handleSubmit}>Add</Button>
```

## TODO List is ready!

Congratulations, you have created your first full-fledged application using StartupJS features.

In the following sections of the tutorial, you will delve deeper into the StartupJS features.
