Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ✨ add application patch feature #168

Merged
merged 1 commit into from
Dec 16, 2024
Merged
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
2 changes: 1 addition & 1 deletion client/.env.development
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VITE_RDA_KEYCLOAK_AUTH_SERVER_URL=http://localhost:8082
VITE_RDA_KEYCLOAK_REALM=referentiel-applications
VITE_RDA_KEYCLOAK_CLIENT_ID=referentiel-applications
VITE_RDA_API_URL=http://localhost:8080
VITE_RDA_API_URL=http://localhost:3500
70 changes: 31 additions & 39 deletions client/src/components/ApplicationOverview.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
import type { Application } from "@/models/Application";
import { ref } from "vue";
import Description from "./Description.vue";
import InformationsGenerales from "./InformationsGenerales.vue";
import Objectifs from "./Objectifs.vue";
import Tags from "./Tags.vue";
Expand All @@ -13,46 +12,39 @@ const activeTab = ref(0);
const tabListName = "Informations sur l’application";
const tabTitles = [
{ title: "Informations générales", icon: "ri-checkbox-circle-line", tabId: "tab-0", panelId: "tab-content-0" },
{ title: "Description", icon: "ri-checkbox-circle-line", tabId: "tab-1", panelId: "tab-content-1" },
{ title: "Objectifs", icon: "ri-checkbox-circle-line", tabId: "tab-2", panelId: "tab-content-2" },
{ title: "Tags", icon: "ri-checkbox-circle-line", tabId: "tab-3", panelId: "tab-content-3" },
{ title: "Objectifs", icon: "ri-checkbox-circle-line", tabId: "tab-1", panelId: "tab-content-2" },
{ title: "Tags", icon: "ri-checkbox-circle-line", tabId: "tab-2", panelId: "tab-content-3" },
];
</script>

<template>
<div class="fr-container fr-my-2w">
<!-- DsfrTabs pour les onglets avec gestion de l'onglet actif -->
<DsfrTabs v-model="activeTab" :tab-list-name="tabListName">
<!-- Définir chaque onglet via DsfrTabItem -->
<template #tab-items>
<DsfrTabItem
v-for="(tab, index) in tabTitles"
:key="tab.tabId"
:tab-id="tab.tabId"
:panel-id="tab.panelId"
:icon="tab.icon"
@click="activeTab = index"
>
{{ tab.title }}
</DsfrTabItem>
</template>

<!-- Contenu de chaque onglet -->
<DsfrTabContent v-if="activeTab === 0" panel-id="tab-content-0" tab-id="tab-0">
<InformationsGenerales :application="props.application" />
</DsfrTabContent>

<DsfrTabContent v-if="activeTab === 1" panel-id="tab-content-1" tab-id="tab-1">
<Description :application="props.application" />
</DsfrTabContent>

<DsfrTabContent v-if="activeTab === 2" panel-id="tab-content-2" tab-id="tab-2">
<Objectifs :application="props.application" />
</DsfrTabContent>

<DsfrTabContent v-if="activeTab === 3" panel-id="tab-content-3" tab-id="tab-3">
<Tags :application="props.application" />
</DsfrTabContent>
</DsfrTabs>
</div>
<!-- DsfrTabs pour les onglets avec gestion de l'onglet actif -->
<DsfrTabs v-model="activeTab" :tab-list-name="tabListName">
<!-- Définir chaque onglet via DsfrTabItem -->
<template #tab-items>
<DsfrTabItem
v-for="(tab, index) in tabTitles"
:key="tab.tabId"
:tab-id="tab.tabId"
:panel-id="tab.panelId"
:icon="tab.icon"
@click="activeTab = index"
>
{{ tab.title }}
</DsfrTabItem>
</template>

<!-- Contenu de chaque onglet -->
<DsfrTabContent v-if="activeTab === 0" panel-id="tab-content-0" tab-id="tab-0">
<InformationsGenerales :application="props.application" />
</DsfrTabContent>

<DsfrTabContent v-if="activeTab === 1" panel-id="tab-content-1" tab-id="tab-1">
<Objectifs :application="props.application" />
</DsfrTabContent>

<DsfrTabContent v-if="activeTab === 2" panel-id="tab-content-2" tab-id="tab-2">
<Tags :application="props.application" />
</DsfrTabContent>
</DsfrTabs>
</template>
14 changes: 0 additions & 14 deletions client/src/components/Description.vue

This file was deleted.

36 changes: 29 additions & 7 deletions client/src/components/InformationsGenerales.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,37 @@
<script setup lang="ts">
import type { Application } from "@/models/Application";
import useToaster from "@/composables/use-toaster";
import axios from "axios";

const props = defineProps<{ application: Application }>();
const loading = ref(false);
const toaster = useToaster();

function patchApplication(event: Event) {
loading.value = true;
axios
.patch(`/applications/${props.application.id}`, {
label: props.application.label,
shortName: props.application.shortName,
description: props.application.description,
})
.then(() => {
toaster.addSuccessMessage("Application mise à jour avec succès");
})
.catch((error) => {
toaster.addErrorMessage("Erreur lors de la mise à jour de l'application");
})
.finally(() => {
loading.value = false;
});
}
</script>

<template>
<div>
<h3>Informations générales</h3>
<p><strong>Identifiant unique :</strong> {{ application.id }}</p>
<p><strong>Nom court :</strong> {{ application.label }}</p>
<p><strong>Nom abrégé :</strong> {{ application.shortName }}</p>
<p><strong>Logo :</strong> {{ application.logo }}</p>
</div>
<h3>Informations générales</h3>
<div class="fr-mb-3w"><DsfrInput v-model="application.id" label="Identifiant unique" label-visible disabled /></div>
<div class="fr-mb-3w"><DsfrInput v-model="application.label" label="Nom" label-visible required /></div>
<div class="fr-mb-3w"><DsfrInput v-model="application.shortName" label="Nom abrégé" label-visible required /></div>
<div class="fr-mb-3w"><DsfrInput v-model="application.description" label="Description" label-visible required isTextarea /></div>
<DsfrButton label="Sauvegarder" primary @click="patchApplication" />
</template>
2 changes: 1 addition & 1 deletion client/src/components/SearchApplications.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ async function doSearch() {
isLoading.value = true;
errorMessage.value = "";
const results = await Applications.getAllApplicationBySearch(searchTerm.value || "");
searchResults.value = results?.collection || [];
searchResults.value = results || [];
} catch (error) {
errorMessage.value = "Une erreur est survenue lors du chargement des applications.";
} finally {
Expand Down
1 change: 1 addition & 0 deletions client/src/models/Application.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export interface Application {
id: string;
label: string;
shortName?: string;
description?: string;
organisationCode?: string;
createdAt: string;
Expand Down
15 changes: 13 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ services:
- ALLOWED_ORIGINS=http://localhost:5173,http://localhost:8080,http://krakend:8080
- KEYCLOAK_BASE_URL=http://localhost:8082
- KEYCLOAK_REALM=referentiel-applications
# To be deleted
- KEYCLOAK_CLIENT_SECRET="client secret"
depends_on:
- postgres
ports:
Expand All @@ -34,6 +32,19 @@ services:
- ./server:/app
- /app/node_modules

prisma-studio:
build:
context: ./server
command: npx prisma studio
environment:
- DATABASE_URL=postgresql://postgres:password@postgres:5432/postgres
- ALLOWED_ORIGINS=http://localhost:5173,http://localhost:8080,http://krakend:8080
- KEYCLOAK_BASE_URL=http://localhost:8082
depends_on:
- postgres
ports:
- "5555:5555"

keycloak:
build:
context: ./keycloak
Expand Down
50 changes: 39 additions & 11 deletions krakend.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,24 @@
"name": "krakend",
"extra_config": {
"security/cors": {
"allow_origins": ["http://localhost:5173"],
"allow_methods": ["GET", "HEAD", "POST", "OPTIONS"],
"allow_origins": ["http://localhost:5173", "http://localhost:8080"],
"allow_methods": [
"GET",
"HEAD",
"POST",
"OPTIONS",
"PUT",
"PATCH",
"DELETE"
],
"expose_headers": ["Content-Length", "Content-Type"],
"allow_headers": [
"Accept-Language",
"X-User-Id",
"X-User-Email",
"Authorization",
"Content-Type"
"Content-Type",
"Origin"
],
"max_age": "12h",
"allow_credentials": true,
Expand Down Expand Up @@ -49,27 +58,46 @@
]
},
{
"endpoint": "api/v2/{ressource}",
"output_encoding": "no-op",
"endpoint": "api/v2/applications",
"method": "POST",
"backend": [
{
"encoding": "no-op",
"host": ["http://backend:3500"],
"url_pattern": "api/v2/{ressource}"
"url_pattern": "api/v2/applications",
"method": "POST"
}
],
"output_encoding": "no-op",
"input_headers": [
"Authorization",
"X-User",
"X-Role",
"ClientId",
"X-Requested-With",
"Content-Type",
"Content-Length"
]
},
{
"endpoint": "api/v2/applications",
"method": "POST",
"endpoint": "api/v2/applications/{id}",
"method": "PATCH",
"backend": [
{
"host": ["http://backend:3500"],
"url_pattern": "api/v2/applications",
"method": "POST"
"method": "PATCH"
}
],
"output_encoding": "no-op"
"output_encoding": "no-op",
"input_headers": [
"Authorization",
"X-User",
"X-Role",
"ClientId",
"X-Requested-With",
"Content-Type",
"Content-Length"
]
},
{
"endpoint": "api/v2/applications/search",
Expand Down
3 changes: 2 additions & 1 deletion server/nest-cli.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
"deleteOutDir": true,
"plugins": ["@nestjs/swagger"]
}
}
1 change: 1 addition & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"jwks-rsa": "^3.1.0",
"lodash-es": "^4.17.21",
"moment": "^2.30.1",
"nestjs-prisma": "^0.23.0",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"pg": "^8.11.3",
Expand Down
Loading
Loading