Skip to content

Commit

Permalink
Move from Vue & Bootstrap-Vue to Nuxt & Vuetify and new Login page
Browse files Browse the repository at this point in the history
  • Loading branch information
pablosnt committed Apr 22, 2024
1 parent 3a49fce commit f26ac0c
Show file tree
Hide file tree
Showing 32 changed files with 12,830 additions and 2,155 deletions.
9 changes: 4 additions & 5 deletions .github/workflows/code-style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ jobs:

- name: Install ESLint
working-directory: src/frontend
run: |
npm install .
npm install -g eslint
run: npm install .

- uses: dorny/paths-filter@3c49e64ca26115121162fb767bc6af9e8d059f1a
id: changes
Expand All @@ -68,6 +66,7 @@ jobs:
frontend:
- 'src/frontend/**'
- name: ESLint check
- name: StyleLint check
working-directory: src/frontend/
if: ${{ steps.changes.outputs.frontend == 'true' || github.event_name != 'pull_request' }}
run: eslint src/frontend/ --ext .js,.jsx,.ts,.tsx
run: npm run stylelint
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ dist_electron/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.nuxt/
# Editor directories and files
*.suo
*.ntvs*
Expand Down
1 change: 0 additions & 1 deletion src/frontend/.env.production

This file was deleted.

14 changes: 0 additions & 14 deletions src/frontend/.eslintrc.cjs

This file was deleted.

8 changes: 0 additions & 8 deletions src/frontend/.prettierrc.json

This file was deleted.

3 changes: 3 additions & 0 deletions src/frontend/.stylelintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "stylelint-config-standard"
}
72 changes: 56 additions & 16 deletions src/frontend/README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,75 @@
# frontend
# Nuxt 3 Minimal Starter

This template should help get you started developing with Vue 3 in Vite.
Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.

## Recommended IDE Setup
## Setup

[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
Make sure to install the dependencies:

## Customize configuration
```bash
# npm
npm install

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

## Project Setup
# yarn
yarn install

```sh
npm install
# bun
bun install
```

### Compile and Hot-Reload for Development
## Development Server

```sh
Start the development server on `http://localhost:3000`:

```bash
# npm
npm run dev

# pnpm
pnpm run dev

# yarn
yarn dev

# bun
bun run dev
```

### Compile and Minify for Production
## Production

Build the application for production:

```sh
```bash
# npm
npm run build

# pnpm
pnpm run build

# yarn
yarn build

# bun
bun run build
```

### Lint with [ESLint](https://eslint.org/)
Locally preview production build:

```bash
# npm
npm run preview

```sh
npm run lint
# pnpm
pnpm run preview

# yarn
yarn preview

# bun
bun run preview
```

Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
7 changes: 7 additions & 0 deletions src/frontend/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<template>
<NuxtLayout>
<v-app>
<NuxtPage />
</v-app>
</NuxtLayout>
</template>
16 changes: 16 additions & 0 deletions src/frontend/composables/alerts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { toast } from 'vue3-toastify';
import 'vue3-toastify/dist/index.css';


export function useAlert() {
function alert(message: string, type: string) {
toast(message, {
'theme': 'colored',
'type': type,
'position': 'bottom-right',
'transition': 'slyde'
})
}

return alert
}
175 changes: 175 additions & 0 deletions src/frontend/composables/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
export function useApi(endpoint: string, authentication: boolean = true, refreshData: boolean = true, refreshAll: boolean = false, entity?: string, extraHeaders?: object) {
const config = useRuntimeConfig()
const router = useRouter()
const refreshing = refreshStore()
const alert = useAlert()
const { getTokens, saveTokens, removeTokens } = useTokens()

const defaultHeaders = {
'Content-Type': 'application/json',
Accept: 'application/json'
}
// TODO: Separate pagination, it must be provided by the parent item
let { page, limit, total, limits, max_limit } = usePagination()

let data = ref({})
let items = ref({})

function url(endpoint: string): string {
const currentEndpoint = config.backendRootPath ? config.backendRootPath + endpoint : endpoint
if (config.backendUrl) {
var url = new URL(config.backendUrl)
url.pathname = currentEndpoint
return url.href
}
return currentEndpoint
}

function headers(authentication: boolean): object {
let currentHeaders = defaultHeaders
const token = getTokens().access
if (authentication && token) {
currentHeaders.Authorization = `Bearer ${token}`
}
return Object.assign({}, currentHeaders, extraHeaders)
}

function request(endpoint: string, options = {}): Promise<any> {
return useFetch(url(endpoint), options)
.then((response) => {
if (response.error.value) {
throw createError(response.error.value)
}
return Promise.resolve(response.data.value)
})
.catch((error) => {
let message = 'Unexpected error'
switch (error.statusCode) {
case 400:
// TODO
console.log(error.data)
// const value = Object.values(data)[0][0]
// message = `${Object.keys(data)[0]}: ${value.charAt(0).toUpperCase}${value.slice(1)}`
message = error.data
break
case 401:
if (endpoint === '/api/security/refresh/') {
message = null
} else if (authentication) {
refresh()
return request(endpoint, options)
} else {
message = 'Invalid credentials'
}
break
case 403:
message = 'You are not authorized to perform this operation'
break
case 404:
message = 'Resource not found'
break
}
if (message) {
alert(message, 'error')
}
return Promise.reject(error)
})
}

function refresh() {
if (!refreshing.refreshing) {
refreshing.change()
request('/api/security/refresh/', { method: 'POST', headers: headers(true), body: { refresh: getTokens().refresh }})
.then((response) => {
removeTokens()
saveTokens(response)
refreshing.change()
})
.catch((error) => {
removeTokens()
refreshing.change()
router.push({ name: 'login' })
})
}
}

function get(id: number): Promise<any> {
return request(id ? `${endpoint}${id}/` : endpoint, { method: 'GET', headers: headers(authentication) })
.then((response) => {
data = response
return Promise.resolve(response)
})
}

function list(all = false, currentPage = 1): Promise<any> {
const currentLimit = all ? max_limit : limit
currentPage = all ? currentPage : page
if (currentPage === 1 && all) {
items = []
}
// TODO: filter params
return request(endpoint, { method: 'GET', headers: headers(authentication), params: { page: currentPage, limit: currentLimit } })
.then((response) => {
total = response.count
items = items.concat(response.results)
if ((currentPage * currentLimit) < total && all) {
return list(all, currentPage + 1)
} else {
return Promise.resolve(items)
}
})
}

function create(body: object) {
return request(endpoint, { method: 'POST', headers: headers(authentication), body: body })
.then((response) => {
if (response) {
if (items && refreshData) {
list(refreshAll)
}
if (entity) {
alert(`New ${entity.toLowerCase()} has been successfully created`, 'success')
}
}
return Promise.resolve(response)
})
}

function update(id: number, body: object) {
return request(id ? `${endpoint}${id}/` : endpoint, { method: 'PUT', headers: headers(authentication), body: body })
.then((response) => {
if (response) {
if (refreshData) {
if (data) {
data = response
}
if (items) {
list(refreshAll)
}
}
if (entity) {
alert(`${entity} has been successfully updated`, 'success')
}
}
return Promise.resolve(response)
})
}

function remove(id: number) {
return request(id ? `${endpoint}${id}/` : endpoint, { method: 'DELETE', headers: headers(authentication) })
.then((response) => {
if (response) {
if (refreshData && items) {
list(refreshAll)
}
if (entity) {
alert(`${entity} has been deleted`, 'warning')
}
}
return Promise.resolve(response)
})
}

// page, limit, total, limits
return { data, items, get, list, create, update, remove }
}
9 changes: 9 additions & 0 deletions src/frontend/composables/pagination.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function usePagination() {
const page = ref(1)
const limit = ref(25)
const total = ref(null)
const limits = [25, 50, 100]
const max_limit = 1000

return { page, limit, total, limits, max_limit }
}
Loading

0 comments on commit f26ac0c

Please sign in to comment.