Compare commits

...

13 Commits

Author SHA1 Message Date
c32eea8a40 Merge pull request 'Mise en place d'un linter, de formatteur et d'actions afin de vérifier que le code du frontend compile bien' () from linter into main
Reviewed-on: 
Reviewed-by: Theo <tlelez@enseirb-matmeca.fr>
Vu que vous ne voulez pas faire avancer le projet, je vais abuser de mes droits d'administrateur et le faire.
2025-02-10 22:44:34 +01:00
Pierre Tellier
30344a60b7 fix: removed import 2025-02-09 16:12:43 +01:00
Pierre Tellier
2465545b6b fix: removed temp modal 2025-02-09 16:11:54 +01:00
Pierre Tellier
83cbeb7a2e feat: single workflow that check prettier, linter and build 2025-02-09 16:07:40 +01:00
Pierre Tellier
645a10477d feat: now respect codestyle 2025-02-09 15:59:30 +01:00
Pierre Tellier
a859871265 fix: prettier now fail instead of doing nothing 2025-02-09 15:57:40 +01:00
Pierre Tellier
0eab9a8063 feat: implemented prettier 2025-02-09 15:54:19 +01:00
Pierre Tellier
1dff7573ff feat: now compliant with eslint 2025-02-09 15:28:09 +01:00
Pierre Tellier
8af40bfe50 fix: linter action: installed required modules 2025-02-09 14:04:12 +01:00
Pierre Tellier
afa4d34ec8 fix: linter action 2025-02-09 14:02:34 +01:00
Pierre Tellier
c5fc5b600e fix: linter action 2025-02-09 14:00:49 +01:00
Pierre Tellier
a4939737fe feat: trying to setup linter 2025-02-09 13:57:17 +01:00
Pierre Tellier
e7ebcc0d3a feat: created eslint config 2025-02-09 12:36:43 +01:00
21 changed files with 1855 additions and 354 deletions

@ -0,0 +1,24 @@
name: CI
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install everything
working-directory: ./front/MyINPulse-front
run: npm i
- name: Run ESLint
working-directory: ./front/MyINPulse-front
run: npx eslint
- name: Run prettier
working-directory: ./front/MyINPulse-front
run: npx prettier src --check
- name: Build frontend
working-directory: ./front/MyINPulse-front
run: npm run build

@ -36,4 +36,4 @@ playwright-report/
# Custom
.installed
package-lock.json
./package-lock.json

@ -0,0 +1,7 @@
{
"useTabs":false,
"semi":true,
"trailingComma":"es5",
"arrowParens":"always",
"tabWidth":4
}

@ -0,0 +1,29 @@
import eslint from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
import eslintPluginVue from "eslint-plugin-vue";
import globals from "globals";
import typescriptEslint from "typescript-eslint";
export default typescriptEslint.config(
{ ignores: ["*.d.ts", "**/coverage", "**/dist"] },
{
extends: [
eslint.configs.recommended,
...typescriptEslint.configs.recommended,
...eslintPluginVue.configs["flat/recommended"],
],
files: ["**/*.{ts,vue}"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: globals.browser,
parserOptions: {
parser: typescriptEslint.parser,
},
},
rules: {
// your rules
},
},
eslintConfigPrettier
);

File diff suppressed because it is too large Load Diff

@ -26,8 +26,15 @@
"@types/node": "^22.10.7",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/tsconfig": "^0.7.0",
"eslint": "^9.20.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-vue": "^9.32.0",
"globals": "^15.14.0",
"jiti": "^2.4.2",
"npm-run-all2": "^7.0.2",
"prettier": "3.5.0",
"typescript": "~5.7.3",
"typescript-eslint": "^8.23.0",
"vite": "^6.0.11",
"vite-plugin-vue-devtools": "^7.7.0",
"vue-tsc": "^2.2.0"

@ -1,50 +1,47 @@
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import { RouterView } from "vue-router";
import ErrorWrapper from "@/views/errorWrapper.vue";
import ProjectComponent from "@/components/ProjectComponent.vue";
</script>
<template>
<Header />
<error-wrapper></error-wrapper>
<div id="main">
<ProjectComp
v-for="(project, index) in projects"
:key="index"
:projectName="project.name"
/>
<HeaderComponent />
<error-wrapper></error-wrapper>
<div id="main">
<ProjectComponent
v-for="(project, index) in projects"
:key="index"
:project-name="project.name"
/>
</div>
<RouterView />
<RouterView />
</template>
<style scoped>
</style>
<script lang="ts">
import Header from "@/components/Header.vue";
import ProjectComp from "@/components/Project-comp.vue";
import HeaderComponent from "@/components/HeaderComponent.vue";
export default {
name: 'App',
components: {
Header,
ProjectComp,
},
data() {
return {
projects: [
{
name: 'Projet Alpha',
//link: './project-alpha.html',
//members: ['Alice', 'Bob', 'Charlie'],
},
{
name: 'Projet Beta',
//link: './project-beta.html',
//members: ['David', 'Eve', 'Frank'],
},
],
};
},
name: "App",
components: {
HeaderComponent,
},
data() {
return {
projects: [
{
name: "Projet Alpha",
//link: './project-alpha.html',
//members: ['Alice', 'Bob', 'Charlie'],
},
{
name: "Projet Beta",
//link: './project-beta.html',
//members: ['David', 'Eve', 'Frank'],
},
],
};
},
};
</script>
</script>
<style scoped></style>

@ -1,26 +1,26 @@
<template>
<header>
<img src="./icons/logo inpulse.png" alt="INPulse" />
<img src="./icons/logo inpulse.png" alt="INPulse" />
</header>
</template>
<script lang="ts">
export default {
name: 'Header',
};
</script>
<style scoped>
header img {
width: 100px;
}
</template>
header{
<script lang="ts">
export default {
name: "HeaderComponent",
};
</script>
<style scoped>
header img {
width: 100px;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
background-color: #fff;
border-bottom: 2px solid #ddd;
}
</style>
}
</style>

@ -3,16 +3,19 @@
<div class="project-header">
<h2>{{ projectName }}</h2>
</div>
</div>
</template>
<script lang="ts">
import type { PropType } from "vue";
export default {
name: 'Project',
name: "ProjectComponent",
props: {
projectName: String,
}
}
</script>
projectName: {
type: Object as PropType<string>,
required: true,
},
},
};
</script>

@ -1,17 +1,43 @@
<script setup lang="ts">
const props = defineProps(['data']);
import { color } from "@/services/popupDisplayer.ts";
import type { PropType } from "vue";
defineProps({
errorMessage: {
type: Object as PropType<string>,
required: true,
},
errorColor: {
type: Object as PropType<color>,
default: color.Red,
},
});
</script>
<template>
<div :class='["red", "yellow", "blue", "green"][data.type]' class="error-modal">
<p>{{["Erreur :(", "Warning :|", "Info :)", "Succes ;)"][data.type]}}</p>
<p>{{data.errorMessage}}</p>
<div class="loading" :class='["red-loader", "yellow-loader", "blue-loader", "green-loader"][data.type]'></div>
</div>
<div
:class="['red', 'yellow', 'blue', 'green'][errorColor]"
class="error-modal"
>
<p>
{{
["Erreur :(", "Warning :|", "Info :)", "Succes ;)"][errorColor]
}}
</p>
<p>{{ errorMessage }}</p>
<div
class="loading"
:class="
['red-loader', 'yellow-loader', 'blue-loader', 'green-loader'][
errorColor
]
"
></div>
</div>
</template>
<style scoped>
.error-modal{
.error-modal {
margin-bottom: 1em;
padding: 1em;
border-radius: 1em;
@ -19,41 +45,45 @@ const props = defineProps(['data']);
overflow: hidden;
position: relative;
animation: disappear 5s linear forwards;
}
}
.red{
.red {
background-color: #ee6055;
color: white;
}
.red-loader {
}
.red-loader {
background-color: #fa8383;
}
}
.yellow{
background-color: #FF9D23;
color: white;
}
.yellow-loader{
.yellow {
background-color: #ff9d23;
color: white;
}
.yellow-loader {
background-color: #ffbf81;
}
}
.blue {
.blue {
background-color: #809bce;
color: white;
}
.blue-loader{
background-color: #95b8d1;
}
}
.green {
.blue-loader {
background-color: #95b8d1;
}
.green {
background-color: green;
color: white;
}
.green-loader {
background-color: darkgreen;
}
}
.loading {
.green-loader {
background-color: darkgreen;
}
.loading {
box-sizing: border-box;
position: absolute;
padding: 0;
@ -62,33 +92,33 @@ const props = defineProps(['data']);
height: 1em;
width: 0;
animation: loading 4s linear forwards;
}
}
/* Animation for the loading bar */
@keyframes loading {
/* Animation for the loading bar */
@keyframes loading {
0% {
width: 100%;
width: 100%;
}
100% {
width: 0;
width: 0;
}
}
}
@keyframes disappear {
@keyframes disappear {
0% {
height: 100px;
padding: 1em;
margin: 1em;
height: 100px;
padding: 1em;
margin: 1em;
}
80% {
height: 100px;
padding: 1em;
margin: 1em;
height: 100px;
padding: 1em;
margin: 1em;
}
100% {
height: 0;
margin: 0;
padding: 0;
height: 0;
margin: 0;
padding: 0;
}
}
</style>
}
</style>

@ -1,36 +1,31 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/router.ts'
import {createPinia} from "pinia";
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
import AuthStorePlugin from './plugins/authStore';
import keycloakService from './services/keycloak';
import {useAuthStore} from "@/stores/authStore.ts";
let store: any;
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router/router.ts";
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
import keycloakService from "./services/keycloak";
import { type AuthStore, useAuthStore } from "@/stores/authStore.ts";
let store: AuthStore;
keycloakService.CallInit(() => {
try {
const app = createApp(App)
const app = createApp(App);
// Setup pinia store, allowing user to keep logged in status after refresh
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
app.use(pinia);
app.use(AuthStorePlugin, { pinia });
store = useAuthStore();
app.use(router)
keycloakService.CallInitStore(store);
app.use(router);
app.mount('#app');
app.mount("#app");
} catch (e) {
console.error("Error while initiating Keycloak.")
console.error(e)
createApp(App).mount('#app');
console.error("Error while initiating Keycloak.");
console.error(e);
createApp(App).mount("#app");
}
});
})
export {store};
export { store };

@ -1,14 +0,0 @@
// file: src/plugins/authStore.js
import { useAuthStore } from "@/stores/authStore.ts";
import keycloakService from '@/services/keycloak';
// Setup auth store as a plugin so it can be accessed globally in our FE
const authStorePlugin = {
install(app: any, option: any) {
const store = useAuthStore(option.pinia);
app.config.globalProperties.$store = store;
keycloakService.CallInitStore(store);
}
}
export default authStorePlugin;

@ -1,17 +1,17 @@
import { createRouter, createWebHistory } from 'vue-router'
import { createRouter, createWebHistory } from "vue-router";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/test',
name: 'test',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/test.vue'),
},
],
})
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/test",
name: "test",
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import("../views/testComponent.vue"),
},
],
});
export default router
export default router;

@ -1,31 +1,36 @@
import axios from "axios";
import {store} from "@/main.ts";
import {addNewMessage, color} from "@/services/popupDisplayer.ts";
import axios, { type AxiosError, type AxiosResponse } from "axios";
import { store } from "@/main.ts";
import { addNewMessage, color } from "@/services/popupDisplayer.ts";
const axiosInstance = axios.create({
baseURL: import.meta.env.VITE_BACKEND_URL,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
});
axiosInstance.interceptors.response.use(
response => response, // Directly return successful responses.
async error => {
(response) => response, // Directly return successful responses.
async (error) => {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry && store.authenticated) {
if (
error.response.status === 401 &&
!originalRequest._retry &&
store.authenticated
) {
originalRequest._retry = true; // Mark the request as retried to avoid infinite loops.
try {
await store.refreshUserToken();
// Update the authorization header with the new access token.
axiosInstance.defaults.headers.common['Authorization'] = `Bearer ${store.user.token}`;
axiosInstance.defaults.headers.common["Authorization"] =
`Bearer ${store.user.token}`;
return axiosInstance(originalRequest); // Retry the original request with the new access token.
} catch (refreshError) {
// Handle refresh token errors by clearing stored tokens and redirecting to the login page.
console.error('Token refresh failed:', refreshError);
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
window.location.href = '/login';
console.error("Token refresh failed:", refreshError);
localStorage.removeItem("accessToken");
localStorage.removeItem("refreshToken");
window.location.href = "/login";
return Promise.reject(refreshError);
}
}
@ -34,23 +39,29 @@ axiosInstance.interceptors.response.use(
);
// TODO: spawn a error modal
function defaultApiErrorHandler(err: string){
addNewMessage(err, color.Red);
function defaultApiErrorHandler(err: AxiosError) {
addNewMessage(err.message, color.Red);
}
function defaultApiSuccessHandler(response: any){
addNewMessage(response.data, color.green)
}
function callApi(endpoint: string, onSuccessHandler?: any, onErrorHandler?: any): void {
axiosInstance.get(endpoint).then(
onSuccessHandler == null ? defaultApiSuccessHandler : onSuccessHandler
).catch(
(err) => {
onErrorHandler == null ? defaultApiErrorHandler(err): onErrorHandler(err);
throw err;
}
)
function defaultApiSuccessHandler(response: AxiosResponse) {
addNewMessage(response.data, color.Green);
}
function callApi(
endpoint: string,
onSuccessHandler?: (response: AxiosResponse) => void,
onErrorHandler?: (error: AxiosError) => void
): void {
axiosInstance
.get(endpoint)
.then(
onSuccessHandler == null
? defaultApiSuccessHandler
: onSuccessHandler
)
.catch(
onErrorHandler == null ? defaultApiErrorHandler : onErrorHandler
);
}
export {callApi}
export { callApi };

@ -1,33 +1,31 @@
import Keycloak from 'keycloak-js';
import Keycloak from "keycloak-js";
import type { AuthStore } from "@/stores/authStore.ts";
const options = {
url: import.meta.env.VITE_KEYCLOAK_URL,
clientId: import.meta.env.VITE_KEYCLOAK_CLIENT_ID,
realm: import.meta.env.VITE_KEYCLOAK_REALM
}
realm: import.meta.env.VITE_KEYCLOAK_REALM,
};
const keycloak = new Keycloak(options);
let authenticated: boolean | undefined;
let store = null;
async function login(){
async function login() {
try {
await keycloak.login() // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
await keycloak.login(); // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
return keycloak;
} catch (error) {
console.log(error)
console.log(error);
}
}
async function signup(){
async function signup() {
try {
await keycloak.login(
{action: "register"}
) // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
await keycloak.login({ action: "register" }); // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
return keycloak;
} catch (error) {
console.log(error)
console.log(error);
}
}
@ -42,31 +40,33 @@ async function init(onInitCallback: () => void) {
onLoad: "check-sso",
silentCheckSsoRedirectUri: `${location.origin}/silent-check-sso.htm`,
responseMode: "query",
})
onInitCallback()
});
onInitCallback();
} catch (error) {
console.error("Keycloak init failed")
console.error(error)
console.error("Keycloak init failed");
console.error(error);
}
};
}
/**
* Initializes store with Keycloak user data
*
*/
async function initStore(storeInstance: any) {
async function initStore(storeInstance: AuthStore) {
try {
store = storeInstance
console.log(keycloak)
await store.initOauth(keycloak)
store = storeInstance;
console.log(keycloak);
await store.initOauth(keycloak);
// Show alert if user is not authenticated
if (!authenticated) { console.warn("not authenticated") }
if (!authenticated) {
console.warn("not authenticated");
}
} catch (error) {
console.error("Keycloak init failed")
console.error(error)
console.error("Keycloak init failed");
console.error(error);
}
};
}
/**
* Logout user
@ -83,7 +83,7 @@ async function refreshToken() {
await keycloak.updateToken(480);
return keycloak;
} catch (error) {
console.error('Failed to refresh token');
console.error("Failed to refresh token");
console.error(error);
}
}
@ -97,4 +97,4 @@ const KeycloakService = {
callSignup: signup,
};
export default KeycloakService;
export default KeycloakService;

@ -1,19 +1,43 @@
import {ref} from "vue";
enum color {Red, Yellow, Blue, green}
import { ref, type Ref } from "vue";
function addNewMessage(errorMessage: string, type?: color, timeout?: number){
if (timeout == null){
enum color {
Red,
Yellow,
Blue,
Green,
}
type ErrorMessageContent = {
message: string;
color: color;
id: number;
timeout: number;
};
let id: number = 0;
const getId = () => {
id = id + 1;
return id;
};
function addNewMessage(errorMessage: string, type?: color, timeout?: number) {
if (timeout == null) {
timeout = 5000;
}
if (type == null){
if (type == null) {
type = color.Red;
}
const data = {errorMessage: errorMessage, timeout: timeout, type: type, uid: Math.random()*100000};
errorList.value.push(data)
setTimeout(() => errorList.value.slice(0, 1), timeout)
const data: ErrorMessageContent = {
message: errorMessage,
timeout: timeout,
color: type,
id: getId(),
};
errorList.value.push(data);
setTimeout(() => errorList.value.slice(0, 1), timeout);
}
const errorList: any= ref([])
const errorList: Ref<ErrorMessageContent[]> = ref([]);
export {addNewMessage, errorList, color}
export { addNewMessage, errorList, color, type ErrorMessageContent };

@ -1,54 +1,61 @@
import { defineStore } from "pinia";
import keycloakService from '@/services/keycloak';
import keycloakService from "@/services/keycloak";
import type Keycloak from "keycloak-js";
export const useAuthStore = defineStore("storeAuth", {
const useAuthStore = defineStore("storeAuth", {
state: () => {
return {
testv: true,
authenticated: false,
user: {
token: "",
refreshToken: "",
username: "",
},
}
};
},
persist: true,
getters: {},
actions: {
// Initialize Keycloak OAuth
async initOauth(keycloak: Keycloak, clearData = true) {
if(clearData) { await this.clearUserData(); }
if (clearData) {
await this.clearUserData();
}
this.authenticated = !!keycloak.authenticated; // the !! removes undefined
if (this.authenticated && keycloak.token && keycloak.idTokenParsed && keycloak.refreshToken){
if (
this.authenticated &&
keycloak.token &&
keycloak.idTokenParsed &&
keycloak.refreshToken
) {
this.user.username = keycloak.idTokenParsed.given_name;
this.user.token = keycloak.token;
this.user.refreshToken = keycloak.refreshToken;
}
},
async login(){
async login() {
try {
const keycloak = await keycloakService.callLogin();
if (keycloak)
await this.initOauth(keycloak);
if (keycloak) await this.initOauth(keycloak);
} catch (error) {
console.log(error)
console.log(error);
}
},
async signup() {
try {
const keycloak = await keycloakService.callSignup();
if (keycloak)
await this.initOauth(keycloak);
if (keycloak) await this.initOauth(keycloak);
} catch (error) {
console.log(error)
console.log(error);
}
},
// Logout user
async logout() {
try {
await keycloakService.CallLogout(import.meta.env.VITE_APP_URL + "/test");
await keycloakService.CallLogout(
import.meta.env.VITE_APP_URL + "/test"
);
await this.clearUserData();
} catch (error) {
console.error(error);
@ -58,23 +65,23 @@ export const useAuthStore = defineStore("storeAuth", {
async refreshUserToken() {
try {
const keycloak = await keycloakService.CallTokenRefresh();
if (keycloak)
await this.initOauth(keycloak, false);
if (keycloak) await this.initOauth(keycloak, false);
} catch (error) {
console.error(error);
}
},
test() {
this.testv = !this.testv;
},
// Clear user's store data
clearUserData() {
this.authenticated = false;
this.user = {
token: "",
refreshToken: "",
username: "",
refreshToken: "",
username: "",
};
}
}
});
},
},
});
type AuthStore = ReturnType<typeof useAuthStore>;
export { useAuthStore, type AuthStore };

@ -1,15 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>

@ -1,22 +1,25 @@
<script setup lang="ts">
import {errorList} from "@/services/popupDisplayer.ts";
import { errorList } from "@/services/popupDisplayer.ts";
import ErrorModal from "@/components/errorModal.vue";
</script>
<template>
<div class="error-wrapper">
<error-modal v-for="elm in errorList" :data=elm></error-modal>
</div>
<div class="error-wrapper">
<error-modal
v-for="elm in errorList"
:key="elm.id"
:error-message="elm.message"
:error-color="elm.color"
/>
</div>
</template>
<style scoped>
.error-wrapper{
position: absolute;
left: 70%;
//background-color: blue;
height: 100%;
width: 30%;
.error-wrapper {
position: absolute;
left: 70%;
//background-color: blue;
height: 100%;
width: 30%;
}
</style>
</style>

@ -1,75 +0,0 @@
<script setup lang="ts">
import {store} from "../main.ts";
import {callApi} from "@/services/api.ts";
import ErrorModal from "@/components/errorModal.vue";
import {errorList} from "@/services/popupDisplayer.ts";
import TempModal from "@/components/temp-modal.vue";
import ErrorWrapper from "@/App.vue";
function addResToTable(id: any){
return (req: any) => {
console.log(req)
}
}
</script>
<template>
<h1>Test page</h1>
<table class="test" style="width:100%">
<tbody>
<tr>
<td>Is Currently Authenticated ? </td>
<td>{{store.authenticated}}</td>
<td><button @click="store.login">Login</button></td>
<td><button @click="store.logout">Logout</button></td>
<td><button @click="store.signup">Signup</button></td>
</tr>
<tr>
<td>current token</td>
<td>{{store.user.token}}</td>
<td><button @click="store.refreshUserToken">Refresh</button></td>
</tr>
<tr>
<td>Current refresh token</td>
<td>{{store.user.refreshToken}}</td>
</tr>
<tr>
<td>Entrepreneur API call</td>
<td><button @click="callApi('random')">call</button></td>
<td>res</td>
<td></td>
</tr>
<tr>
<td>Admin API call</td>
<td><button @click="callApi('random2')">call</button></td>
<td>res</td>
<td></td>
</tr>
<tr>
<td>Unauth API call</td>
<td><button @click="callApi('random3')">call</button></td>
<td>res</td>
<td id="3"></td>
</tr>
</tbody>
</table>
<temp-modal></temp-modal>
</template>
<style scoped>
table {
width: 100px;
table-layout: fixed
}
tr {
width: 100%;
height: 100%;
}
td {
border: solid 1px black;
width: 20%;
height: 100%;
overflow: hidden;
}
</style>

@ -0,0 +1,79 @@
<script lang="ts" setup>
import { store } from "../main.ts";
import { callApi } from "@/services/api.ts";
</script>
<template>
<h1>Test page</h1>
<table class="test" style="width: 100%">
<tbody>
<tr>
<td>Is Currently Authenticated ?</td>
<td>{{ store.authenticated }}</td>
<td>
<button @click="store.login">Login</button>
</td>
<td>
<button @click="store.logout">Logout</button>
</td>
<td>
<button @click="store.signup">Signup</button>
</td>
</tr>
<tr>
<td>current token</td>
<td>{{ store.user.token }}</td>
<td>
<button @click="store.refreshUserToken">Refresh</button>
</td>
</tr>
<tr>
<td>Current refresh token</td>
<td>{{ store.user.refreshToken }}</td>
</tr>
<tr>
<td>Entrepreneur API call</td>
<td>
<button @click="callApi('random')">call</button>
</td>
<td>res</td>
<td></td>
</tr>
<tr>
<td>Admin API call</td>
<td>
<button @click="callApi('random2')">call</button>
</td>
<td>res</td>
<td></td>
</tr>
<tr>
<td>Unauth API call</td>
<td>
<button @click="callApi('random3')">call</button>
</td>
<td>res</td>
<td id="3"></td>
</tr>
</tbody>
</table>
</template>
<style scoped>
table {
width: 100px;
table-layout: fixed;
}
tr {
width: 100%;
height: 100%;
}
td {
border: solid 1px black;
width: 20%;
height: 100%;
overflow: hidden;
}
</style>