Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* eslint-env node */
require("@rushstack/eslint-patch/modern-module-resolution");

module.exports = {
root: true,
extends: [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/eslint-config-prettier",
],
rules: {
"prettier/prettier": 0,
"vue/multi-word-component-names": "off",
},
parserOptions: {
ecmaVersion: "latest",
},
};
28 changes: 28 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
.DS_Store
dist
dist-ssr
coverage
*.local

/cypress/videos/
/cypress/screenshots/

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
1 change: 1 addition & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
3 changes: 3 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}
56 changes: 31 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
# Frontend Challenge

#### **_Drafted Architecture_**

![Sketch](src/assets/tree.png)
Our goal is to understand:

- How knowledgeable you are
- How well you structure your app
- How well can you visually reproduce a mockup

Some restrictions:

- Use the Vue javascript framework and its Component system
- Don't use a UI framework like Bootstrap

Expand All @@ -17,7 +22,7 @@ Please fork this repo and commit your changes to the fork.

Here is a preview of what we are expecting:

![Sample](_README_assets/sample.png)
![Sample](src/assets/_README_assets/sample.png)

Keep in mind that we may ask you about some of the decisions you made while building the app.

Expand All @@ -35,36 +40,37 @@ Inside this repository you'll find a folder named `assets-for-challenge`. Inside

## Detailed comments on each element

![Elements Numbered](_README_assets/challenge-elements-numbered.png)

**Functionality**
The app should let the user do the following:
- **1** - _Get data_ button. When the user **clicks on this button**, your app will **get the data from the URL** in [Data source](#Data source) and fill the list
- **2** - _Upload data_ button. When the user **clicks on this button**, your app will **make a POST request to the same url** as the _Get data_ button. You should send the latest information on the list using the same structure as you have received it.
- **3** - _Custom checkbox_. When the user **clicks on the checkbox**, it will become checked. It will not have any other functionality.
- **4** - _Add line_ button. When the user **clicks on this button**, a **new row will be added to the list** and the user should be able to **write text in both fields**.
- **5** - _Delete row_ button. This button should be **visible only when hovering** each row item. When the user **clicks on this button**, your app will **remove that row** from the list.
- **6** - List _item title_ **This title should be editable**.
- **7** - List _item content_. **This content should be editable**.
- **8** - _App title_. No functionality.
- **9** - _Person icon_ linked to each row. No functionality.
Notes:
![Elements Numbered](src/assets/_README_assets/challenge-elements-numbered.png)

**Functionality**

The app should let the user do the following:

- **1** - _Get data_ button. When the user **clicks on this button**, your app will **get the data from the URL** in [Data source](#Data source) and fill the list
- **2** - _Upload data_ button. When the user **clicks on this button**, your app will **make a POST request to the same url** as the _Get data_ button. You should send the latest information on the list using the same structure as you have received it.
- **3** - _Custom checkbox_. When the user **clicks on the checkbox**, it will become checked. It will not have any other functionality.
- **4** - _Add line_ button. When the user **clicks on this button**, a **new row will be added to the list** and the user should be able to **write text in both fields**.
- **5** - _Delete row_ button. This button should be **visible only when hovering** each row item. When the user **clicks on this button**, your app will **remove that row** from the list.
- **6** - List _item title_ **This title should be editable**.
- **7** - List _item content_. **This content should be editable**.
- **8** - _App title_. No functionality.
- **9** - _Person icon_ linked to each row. No functionality.

Notes:

- Don't worry about the blank state before loading the content.

**Style Attributes**

These are some and useful details about some of the elements

- **3** - The checkbox is 16px wide and 16px high. The purple color is `#859EFF`
- **6** - The font-family of the _item title_ is `Montserrat`, the weight is `SemiBold`, the size is `16px`, and the color is `#566074`
- **7** - The font-family of the _item content_ is `Open Sans`, the weight is `Regular`, the size is `16px`, and the color is `#778195`
- **8** - The font-family of the _app title_ is `Montserrat`, the weight is `Medium`, the size is `18px`, and the color is `#414C5E`
- **1** - The checkbox is 16px wide and 16px high. The purple color is `#859EFF`
- **2** - The font-family of the _item title_ is `Montserrat`, the weight is `SemiBold`, the size is `16px`, and the color is `#566074`
- **3** - The font-family of the _item content_ is `Open Sans`, the weight is `Regular`, the size is `16px`, and the color is `#778195`
- **4** - The font-family of the _app title_ is `Montserrat`, the weight is `Medium`, the size is `18px`, and the color is `#414C5E`

The data above is enough to complete challenge. We also include the source Sketch file in the repository, in case you want to check the original file. You can find it in `_README_assets/Challenge.sketch`
The data above is enough to complete challenge. We also include the source Sketch file in the repository, in case you want to check the original file.
You can find it in `src/assets/_README_assets/Challenge.sketch`

## Typography

Expand All @@ -76,4 +82,4 @@ The data above is enough to complete challenge. We also include the source Sketc
- Keeping in mind accessibility
- Adding tests to your components and app
- Using a store (Vuex) to manage the application data
- Using vue-router to prepare the app for a future where it would have multiple routes
- Using vue-router to prepare the app for a future where it would have multiple routes
35 changes: 35 additions & 0 deletions app-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# .

This template should help get you started developing with Vue 3 in Vite.

## Recommended IDE Setup

[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).

## Customize configuration

See [Vite Configuration Reference](https://vitejs.dev/config/).

## Project Setup

```sh
yarn
```

### Compile and Hot-Reload for Development

```sh
yarn dev
```

### Compile and Minify for Production

```sh
yarn build
```

### Lint with [ESLint](https://eslint.org/)

```sh
yarn lint
```
31 changes: 31 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="./src/assets/favicon/favicon.ico" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="./src/assets/favicon/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="./src/assets/favicon/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="./src/assets/favicon/favicon-16x16.png"
/>
<link rel="manifest" href="./src/assets/favicon/site.webmanifest" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Unbabel CRUD</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
29 changes: 29 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "unbabel-assessment",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
},
"dependencies": {
"pinia": "^2.0.26",
"vue": "^3.2.45",
"vue-router": "^4.1.6"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.1.4",
"@vitejs/plugin-vue": "^3.2.0",
"@vitejs/plugin-vue-jsx": "^2.1.1",
"@vue/eslint-config-prettier": "^7.0.0",
"autoprefixer": "^10.4.13",
"eslint": "^8.22.0",
"eslint-plugin-vue": "^9.3.0",
"postcss": "^8.4.19",
"prettier": "^2.7.1",
"tailwindcss": "^3.2.4",
"vite": "^3.2.4"
}
}
6 changes: 6 additions & 0 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Binary file added public/favicon.ico
Binary file not shown.
11 changes: 11 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<template>
<TheHeader :handleFetch="fetchTransactions" :handlePost="sendTransactions" />
<router-view />
</template>

<script setup>
import TheHeader from "./components/TheHeader.vue";

import { useTransactionStore } from "./stores/transactionStore";
const { fetchTransactions, sendTransactions } = useTransactionStore();
</script>
File renamed without changes
Binary file added src/assets/favicon/android-chrome-192x192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/favicon/android-chrome-512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/favicon/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/favicon/favicon-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/favicon/favicon-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/favicon/favicon.ico
Binary file not shown.
19 changes: 19 additions & 0 deletions src/assets/favicon/site.webmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "unbabel-assessment",
"short_name": "unbabel",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
21 changes: 21 additions & 0 deletions src/assets/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&family=Open+Sans:wght@300;400;500;600;700&display=swap");

@tailwind base;
@tailwind components;
@tailwind utilities;

*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
position: relative;
font-weight: normal;
}

body {
width: 100%;
height: 100vh;
background: #f2f2f2;
}
Binary file added src/assets/tree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
88 changes: 88 additions & 0 deletions src/components/BaseToast.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<template>
<Transition name="toast">
<div
id="toast-danger"
role="alert"
class="container mx-auto fixed top-0 left-0 right-0 flex items-center justify-evenly w-full max-w-xs p-4 mb-4 text-gray-500 bg-slate-300 rounded-lg shadow"
:class="isToastClosed && 'hidden'"
>
<div
:class="`${
props.success
? 'inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-white rounded-lg bg-green-600'
: 'inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-white rounded-lg bg-red-500'
}`"
>
<svg
aria-hidden="true"
class="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"
></path>
</svg>
<span class="sr-only">Error icon</span>
</div>
<slot></slot>
<button
type="button"
class="bg-white text-slate-500 hover:text-green-500 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8"
data-dismiss-target="#toast-danger"
aria-label="Close"
@click="isToastClosed = !isToastClosed"
>
<span class="sr-only">Close</span>
<svg
aria-hidden="true"
class="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"
></path>
</svg>
</button>
</div>
</Transition>
</template>

<script setup>
import { ref } from "vue";
const props = defineProps({ success: Boolean, error: Boolean });
const isToastClosed = ref(false);
</script>

<style scoped>
.toast-enter-from {
opacity: 0;
transform: translateY("-100px");
}
.toast-enter-to {
opacity: 1;
transform: translateY(0);
}
.toast-enter-active {
transition: all 1s ease-in;
}

.toast-leave-from {
transform: translateY("-100px");
opacity: 1;
}
.toast-leave-to {
transform: translateY(0);
opacity: 0;
}
.toast-leave-active {
transition: all 1s ease-out;
}
</style>
Loading