diff --git a/frontend/index.html b/frontend/index.html index 3cde71a..4a8deca 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -14,6 +14,6 @@ We're sorry but Chorister doesn't work properly without JavaScript enabled. Please enable it to continue.
- + diff --git a/frontend/package.json b/frontend/package.json index 7954ce9..d5695fa 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,7 @@ "@vuelidate/validators": "^2.0.0", "axios": "^0.21.1", "depcheck": "^1.4.3", + "effect": "^2.0.3", "moment": "^2.29.4", "oidc-client": "^1.11.5", "pinia": "^2.0.23", @@ -40,6 +41,7 @@ "@typescript-eslint/parser": "^6.17.0", "eslint": "^8.39.0", "eslint-plugin-vue": "^9.11.0", + "prettier": "^3.1.1", "typescript": "^5.3.3", "vue-tsc": "^1.8.27" }, @@ -51,10 +53,21 @@ }, "extends": [ "plugin:vue/essential", - "eslint:recommended" + "eslint:recommended", + "@vue/typescript/recommended", + "prettier" + ], + "parser": "vue-eslint-parser", + "parserOptions": { + "parser": "@typescript-eslint/parser" + }, + "plugins": [ + "@typescript-eslint", + "prettier" ], "rules": { "no-unused-vars": "off", + "prettier/prettier": "error", "vue/multi-word-component-names": "off" } }, diff --git a/frontend/shims-vue.d.ts b/frontend/shims-vue.d.ts deleted file mode 100644 index 314e3aa..0000000 --- a/frontend/shims-vue.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.vue' { - import Vue from 'vue' - export default Vue -} \ No newline at end of file diff --git a/frontend/src/api.js b/frontend/src/api.js deleted file mode 100644 index 90ed338..0000000 --- a/frontend/src/api.js +++ /dev/null @@ -1,138 +0,0 @@ -import axios from 'axios' -import { useAuth } from "@/stores/authStore"; - -const SERVER_URL = import.meta.env.VITE_APP_BASE_URL + '/api'; -const auth = useAuth(); -const instance = axios.create({ - baseURL: SERVER_URL, - timeout: 5000 -}); -instance.interceptors.request.use( - async config => { - // console.log('Starting Request', JSON.stringify(config, null, 2)) - let accessToken = await auth.getAccessToken() - if(accessToken) { - config.headers.common.Authorization = 'Bearer ' + accessToken - } - return config - } -) - -const functions = { - - getGetConfig: (embeddedAttributeName) => { - return { - transformResponse: [ - function(data) { - return data ? JSON.parse(data)._embedded[embeddedAttributeName] : data; - } - ] - } - }, - - // Generic - - delete: (resourceUri) => instance.delete(resourceUri), - - // Register - - register: (request) => instance.post('registration', request), - - // Invites - - getInvites: () => instance.get('invites', functions.getGetConfig('invites')), - - updateInviteForId: (id, invite) => instance.put('invites/' + id, invite), - - getInviteByToken: (token) => instance.get('invite?token=' + token), - - acceptInvite: (request) => instance.post('invite/accept', request), - - getToken: () => instance.get('choir/invitelink'), - - deleteToken: () => instance.delete('choir/invitelink'), - - // Songs - - getSongById: (id) => instance.get('songs/' + id), - - getAllSongs: () => instance.get('songs', functions.getGetConfig('songs')), - - getSongsByCategoryId: (categoryId) => instance.get('songs/search/bycategory?id=' + categoryId, functions.getGetConfig('songs')), - - createNewSong: (song) => instance.post('songs', song), - - updateSongForId: (id, song) => instance.put('songs/' + id, song), - - deleteSongForId: (id) => instance.delete('songs/' + id), - - // Scores - - getScoresBySongId: (songId) => instance.get('songs/' + songId + '/scores', functions.getGetConfig('scores')), - - createNewScore: (score) => instance.post('scores', score), - - updateScoreForId: (id, score) => instance.put('scores/' + id, score), - - deleteScoreForId: (id) => instance.delete('scores/' + id), - - // Categories - - getCategoryById: (id) => instance.get('categories/' + id), - - getAllCategories: () => instance.get('categories', functions.getGetConfig('categories')), - - // Song categories - - getSongCategories: (songId) => instance.get('songs/' + songId + '/categories', functions.getGetConfig('categories')), - - postSongCategories: (songId, categoryUris) => instance.post('songs/' + songId + '/categories', categoryUris.join('\r\n'), { - headers: { - 'content-type': 'text/uri-list' - } - }), - - deleteSongCategory: (songId, categoryId) => instance.delete('songs/' + songId + '/categories/' + categoryId), - - // Setlists - - getSetlistById: (id) => instance.get('setlists/' + id), - - getAllSetlists: () => instance.get('setlists', functions.getGetConfig('setlists')), - - createNewSetlist: (setlist) => instance.post('setlists', setlist), - - updateSetlistForId: (id, setlist) => instance.put('setlists/' + id, setlist), - - deleteSetlistForId: (id) => instance.delete('setlists/' + id), - - // Setlist songs - - getSetlistEntries: (setlistId) => instance.get('setlists/' + setlistId + '/entries', functions.getGetConfig('setlistEntries')), - - postSetlistEntry: (entry) => instance.post("setlistEntries", entry, { - headers: { - 'content-type': 'application/json' - } - }), - - deleteSetlistEntry: (setlistEntryId) => instance.delete("setlistEntries/" + setlistEntryId), - // My Choir - - getChoirs: () => instance.get('choirs', functions.getGetConfig('choirs')), - - updateChoirForId: (id, choir) => instance.post('choirs/' + id, choir), - - // Users - - getUsers: () => instance.get('users', functions.getGetConfig('users')), - - getUserById: (userId) => instance.get('users/' + userId), - - getUser: () => instance.get('user'), - - updateUserForId: (id, user) => instance.patch('users/' + id, user), - -} - -export default functions; \ No newline at end of file diff --git a/frontend/src/api.ts b/frontend/src/api.ts new file mode 100644 index 0000000..adc7e08 --- /dev/null +++ b/frontend/src/api.ts @@ -0,0 +1,141 @@ +import axios from 'axios' +import { useAuth } from "@/stores/authStore"; +import { Invite, Song, Score, Category, Setlist, SetlistEntry, Choir, User, WithEmbedded, AcceptInvite, NewChoirRegistration } from "@/types"; + +const SERVER_URL = import.meta.env.VITE_APP_BASE_URL + '/api'; +const auth = useAuth(); +const instance = axios.create({ + baseURL: SERVER_URL, + timeout: 5000 +}); +instance.interceptors.request.use( + async config => { + let accessToken = await auth.getAccessToken() + if(accessToken) { + config.headers.common.Authorization = 'Bearer ' + accessToken + } + return config + } +) + +const functions = { + + getGetConfig: (embeddedAttributeName: string) => { + return { + transformResponse: [ + function(data: string) { + return data ? JSON.parse(data)._embedded[embeddedAttributeName] : data; + } + ] + } + }, + + // Generic + + delete: (resourceUri: string) => instance.delete(resourceUri), + + // Register + + register: (request: NewChoirRegistration) => instance.post('registration', request), + + // Invites + + getInvites: () => instance.get>('invites', functions.getGetConfig('invites')), + + updateInviteForId: (id: number, invite: any) => instance.put('invites/' + id, invite), + + getInviteByToken: (token: string) => instance.get('invite?token=' + token), + + acceptInvite: (request: AcceptInvite) => instance.post('invite/accept', request), + + getToken: () => instance.get('choir/invitelink'), + + deleteToken: () => instance.delete('choir/invitelink'), + + // Songs + + getSongById: (id: number) => instance.get('songs/' + id), + + getAllSongs: () => instance.get>('songs', functions.getGetConfig('songs')), + + getSongsByCategoryId: (categoryId: number) => instance.get>('songs/search/bycategory?id=' + categoryId, functions.getGetConfig('songs')), + + createNewSong: (song: any) => instance.post('songs', song), + + updateSongForId: (id: number, song: any) => instance.put('songs/' + id, song), + + deleteSongForId: (id: number) => instance.delete('songs/' + id), + + // Scores + + getScoreById: (scoreId: number) => instance.get('scores/' + scoreId), + + getScoresBySongId: (songId: number) => instance.get>('songs/' + songId + '/scores', functions.getGetConfig('scores')), + + createNewScore: (score: any) => instance.post('scores', score), + + updateScoreForId: (id: number, score: any) => instance.put('scores/' + id, score), + + deleteScoreForId: (id: number) => instance.delete('scores/' + id), + + // Categories + + getCategoryById: (id: number) => instance.get('categories/' + id), + + getAllCategories: () => instance.get>('categories', functions.getGetConfig('categories')), + + // Song categories + + getSongCategories: (songId: number) => instance.get>('songs/' + songId + '/categories', functions.getGetConfig('categories')), + + postSongCategories: (songId: number, categoryUris: string[]) => instance.post('songs/' + songId + '/categories', categoryUris.join('\r\n'), { + headers: { + 'content-type': 'text/uri-list' + } + }), + + deleteSongCategory: (songId: number, categoryId: number) => instance.delete('songs/' + songId + '/categories/' + categoryId), + + // Setlists + + getSetlistById: (id: number) => instance.get('setlists/' + id), + + getAllSetlists: () => instance.get>('setlists', functions.getGetConfig('setlists')), + + createNewSetlist: (setlist: any) => instance.post('setlists', setlist), + + updateSetlistForId: (id: number, setlist: any) => instance.put('setlists/' + id, setlist), + + deleteSetlistForId: (id: number) => instance.delete('setlists/' + id), + + // Setlist songs + + getSetlistEntries: (setlistId: number) => instance.get>>('setlists/' + setlistId + '/entries', functions.getGetConfig('setlistEntries')), + + postSetlistEntry: (entry: any) => instance.post("setlistEntries", entry, { + headers: { + 'content-type': 'application/json' + } + }), + + deleteSetlistEntry: (setlistEntryId: string) => instance.delete("setlistEntries/" + setlistEntryId), + + // My Choir + + getChoirs: () => instance.get>('choirs', functions.getGetConfig('choirs')), + + updateChoirForId: (id: number, choir: any) => instance.post('choirs/' + id, choir), + + // Users + + getUsers: () => instance.get>('users', functions.getGetConfig('users')), + + getUserById: (userId: number) => instance.get('users/' + userId), + + getUser: () => instance.get('user'), + + updateUserForId: (id: number, user: any) => instance.patch('users/' + id, user), + +} + +export default functions; \ No newline at end of file diff --git a/frontend/src/components/AppHeader.vue b/frontend/src/components/AppHeader.vue index 97ef8e9..7fca1f3 100644 --- a/frontend/src/components/AppHeader.vue +++ b/frontend/src/components/AppHeader.vue @@ -47,28 +47,19 @@ - \ No newline at end of file diff --git a/frontend/src/components/ChoirMembers.vue b/frontend/src/components/ChoirMembers.vue index 1eb0908..94e5990 100644 --- a/frontend/src/components/ChoirMembers.vue +++ b/frontend/src/components/ChoirMembers.vue @@ -4,11 +4,7 @@

Members

- +
@@ -22,7 +18,7 @@
Name Email
- + - +
- ERROR: {{ error.value }} + ERROR: {{ error }}
- +
@@ -29,9 +21,11 @@ - + - + @@ -39,15 +33,17 @@ @@ -56,214 +52,154 @@
Title
{{ oneBased(index) }}{{ song.title }}{{ song.title }} + {{ song.composer }} {{ (song.songbook || {}).title }} {{ song.songbookNumber }}{{ song.lastSetlist?.date }}
- + {{ category.name }}
-
- {{ songs.length }} - {{ pluralize(songs) }} + {{ songs.length }} + {{ pluralize(songs.length) }}
- \ No newline at end of file diff --git a/frontend/src/views/SignUp.vue b/frontend/src/views/SignUp.vue index 5a9c565..32ade4d 100644 --- a/frontend/src/views/SignUp.vue +++ b/frontend/src/views/SignUp.vue @@ -2,21 +2,22 @@