front_foundation #5
2
front/MyINPulse-front/.gitignore
vendored
2
front/MyINPulse-front/.gitignore
vendored
@ -36,4 +36,4 @@ playwright-report/
|
||||
# Custom
|
||||
|
||||
.installed
|
||||
package-lock.json
|
||||
./package-lock.json
|
1397
front/MyINPulse-front/package-lock.json
generated
1397
front/MyINPulse-front/package-lock.json
generated
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,26 +0,0 @@
|
||||
<template>
|
||||
<header>
|
||||
<img src="./icons/logo inpulse.png" alt="INPulse" />
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'Header',
|
||||
};
|
||||
</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>
|
@ -1,129 +0,0 @@
|
||||
<template>
|
||||
<div @click="goToLink" class="project">
|
||||
<div class="project-header">
|
||||
<h2 >{{ projectName }}</h2>
|
||||
<div class="project-buttons">
|
||||
<button class="contact-btn">Contact</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-body">
|
||||
<ul>
|
||||
<li v-for="(name, index) in listName" :key="index">{{ name }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from "vue";
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
|
||||
const props = defineProps<{
|
||||
projectName: string;
|
||||
listName: string[];
|
||||
projectLink: string;
|
||||
}>();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const goToLink = () => {
|
||||
if (props.projectLink) {
|
||||
router.push(props.projectLink);
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.project {
|
||||
background: linear-gradient(to right, #f4f4f4, #ffffff);
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
/* Header Styling */
|
||||
.project-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.project-header h2 {
|
||||
font-size: 20px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Button Container */
|
||||
.project-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
|
||||
.info-btn {
|
||||
background-color: #4CAF50;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.info-btn:hover {
|
||||
background-color: #45a049;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.contact-btn {
|
||||
background-color: #007BFF;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.contact-btn:hover {
|
||||
background-color: #0056b3;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.project-body {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.project-body p {
|
||||
font-size: 16px;
|
||||
color: #555;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.project-body ul {
|
||||
list-style-type: disc;
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.project-body ul li {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
|
||||
button {
|
||||
padding: 10px 15px;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
</style>
|
120
front/MyINPulse-front/src/components/canvas/HeaderCanvas.vue
Normal file
120
front/MyINPulse-front/src/components/canvas/HeaderCanvas.vue
Normal file
@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<header>
|
||||
<img src="../icons/logo inpulse.png" alt="INPulse Logo">
|
||||
<div class="header-buttons">
|
||||
<div class="menu">
|
||||
<button class="contact-button" @click="toggleDropdown">Contact</button>
|
||||
<div class="contact-dropdown" v-bind:class="{ 'dropdown-visible': isDropdownOpen }">
|
||||
<button @click="contactAll">Contact All</button>
|
||||
<button v-for="(email, index) in entrepreneurEmails" :key="index">
|
||||
{{ email }}
|
||||
</button>
|
||||
</div>
|
||||
<button class="return-button">
|
||||
mohamed_maoulainine marked this conversation as resolved
Outdated
|
||||
<RouterLink to="/">Return to list project</RouterLink>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
mohamed_maoulainine marked this conversation as resolved
Outdated
piair
commented
Ce n'est pas de la composition API, a changer Ce n'est pas de la composition API, a changer
|
||||
|
||||
piair
commented
Il faut se servir de Il faut se servir de `axiosInstance`, voir https://gitea.piair.dev/piair/MyINPulse/src/branch/main/front/MyINPulse-front/src/services/api.ts.
Cela permet de préconfigurer l'authentification et le host du backend.
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import axios from "axios";
|
||||
|
||||
const isDropdownOpen = ref(false);
|
||||
const entrepreneurEmails = ref([]);
|
||||
mohamed_maoulainine marked this conversation as resolved
Outdated
piair
commented
Vérifier que le type de données renvoyé par le backend correspond bien à ca (ce n'est pas le cas). Vérifier que le type de données renvoyé par le backend correspond bien à ca (ce n'est pas le cas).
|
||||
|
||||
const toggleDropdown = () => {
|
||||
isDropdownOpen.value = !isDropdownOpen.value;
|
||||
console.log("Dropdown toggled:", isDropdownOpen.value); // for debug purposes
|
||||
};
|
||||
|
||||
const fetchEntrepreneurs = async () => {
|
||||
try {
|
||||
const response = await axios.get("http://localhost:5000/entrepreneurs");
|
||||
entrepreneurEmails.value = response.data.map((e: { email: string }) => e.email);
|
||||
mohamed_maoulainine marked this conversation as resolved
Outdated
piair
commented
il faut pas Hardcoder une addresse, elle est dans le fichier .env. Dans tout les cas, cette adresse ne devrait pas être là. il faut pas Hardcoder une addresse, elle est dans le fichier .env. Dans tout les cas, cette adresse ne devrait pas être là.
piair
commented
je viens de voir le message de commit, c'est en effet logique d'utiliser cette donnée. Par contre je suis pas sur que ce soit la bonne méthode pour faire des tests, c'est a discuter. Il serait bien d'appeler mock-data au lieu de fake-data je viens de voir le message de commit, c'est en effet logique d'utiliser cette donnée. Par contre je suis pas sur que ce soit la bonne méthode pour faire des tests, c'est a discuter.
Il serait bien d'appeler mock-data au lieu de fake-data
|
||||
} catch (error) {
|
||||
console.error("Erreur lors de la récupération des entrepreneurs:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const contactAll = () => {
|
||||
alert("Contacter tous les entrepreneurs : " + entrepreneurEmails.value.join(", "));
|
||||
};
|
||||
|
||||
onMounted(fetchEntrepreneurs);
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
@import "@/components/canvas/style-project.css";
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header-buttons {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.contact-button, .return-button {
|
||||
background-color: #000;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.return-button a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.contact-dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
margin-top: 5px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.contact-dropdown button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.contact-dropdown button:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.dropdown-visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
header img {
|
||||
width: 100px;
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
@ -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,30 +1,30 @@
|
||||
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");
|
||||
}
|
||||
|
||||
})
|
||||
@ -74,4 +74,4 @@ app.use(VueKeyCloak,{
|
||||
|
||||
|
||||
|
||||
export {store};
|
||||
export { store };
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
@ -9,7 +9,7 @@ const router = createRouter({
|
||||
// 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'),
|
||||
component: () => import('../views/testComponent.vue'),
|
||||
},
|
||||
|
||||
{
|
||||
@ -27,4 +27,4 @@ const router = createRouter({
|
||||
],
|
||||
})
|
||||
|
||||
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>
|
@ -18,9 +18,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Header from '../components/Header.vue';
|
||||
import Header from '../components/HeaderComponent.vue';
|
||||
import Agenda from "../components/Agenda.vue"
|
||||
import ProjectComp from '../components/Project-comp.vue';
|
||||
import ProjectComp from '../components/ProjectComponent.vue';
|
||||
|
||||
import { ref } from "vue";
|
||||
|
||||
|
@ -1,16 +1,21 @@
|
||||
<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%;
|
||||
@ -19,4 +24,4 @@ import ErrorModal from "@/components/errorModal.vue";
|
||||
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>
|
Loading…
x
Reference in New Issue
Block a user
plutôt utiliser des routerLink au lieu de href
https://stackoverflow.com/questions/52675885/when-to-use-router-link-vs-a