Skip to content

Commit

Permalink
feat: 🚸 rework project selection
Browse files Browse the repository at this point in the history
  • Loading branch information
ArnaudTA committed Oct 4, 2024
1 parent a78d097 commit 8e3ac15
Show file tree
Hide file tree
Showing 31 changed files with 679 additions and 446 deletions.
25 changes: 20 additions & 5 deletions apps/client/cypress/components/specs/repo-form.ct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ describe('RepoForm.vue', () => {

const randomDbSetup = createRandomDbSetup({})
const projectStore = useProjectStore()
projectStore.selectedProject = randomDbSetup.project
projectStore.myProjectsById = {
[randomDbSetup.project.id]: randomDbSetup.project.id,
}
projectStore.setSelectedProject(randomDbSetup.project.id)

cy.mount(RepoForm, { props })

Expand Down Expand Up @@ -95,7 +98,10 @@ describe('RepoForm.vue', () => {

const randomDbSetup = createRandomDbSetup({})
const projectStore = useProjectStore()
projectStore.selectedProject = randomDbSetup.project
projectStore.myProjectsById = {
[randomDbSetup.project.id]: randomDbSetup.project.id,
}
projectStore.setSelectedProject(randomDbSetup.project.id)

cy.mount(RepoForm, { props })

Expand Down Expand Up @@ -185,7 +191,10 @@ describe('RepoForm.vue', () => {

const randomDbSetup = createRandomDbSetup({})
const projectStore = useProjectStore()
projectStore.selectedProject = randomDbSetup.project
projectStore.myProjectsById = {
[randomDbSetup.project.id]: randomDbSetup.project.id,
}
projectStore.setSelectedProject(randomDbSetup.project.id)

cy.mount(RepoForm, { props })

Expand Down Expand Up @@ -230,7 +239,10 @@ describe('RepoForm.vue', () => {

const randomDbSetup = createRandomDbSetup({})
const projectStore = useProjectStore()
projectStore.selectedProject = randomDbSetup.project
projectStore.myProjectsById = {
[randomDbSetup.project.id]: randomDbSetup.project.id,
}
projectStore.setSelectedProject(randomDbSetup.project.id)

cy.mount(RepoForm, { props })

Expand Down Expand Up @@ -269,7 +281,10 @@ describe('RepoForm.vue', () => {

const randomDbSetup = createRandomDbSetup({})
const projectStore = useProjectStore()
projectStore.selectedProject = randomDbSetup.project
projectStore.myProjectsById = {
[randomDbSetup.project.id]: randomDbSetup.project.id,
}
projectStore.setSelectedProject(randomDbSetup.project.id)

cy.mount(RepoForm, { props })

Expand Down
13 changes: 8 additions & 5 deletions apps/client/cypress/components/specs/team-ct.ct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import '@gouvfr/dsfr/dist/dsfr.min.css'
import '@gouvfr/dsfr/dist/utility/icons/icons.min.css'
import '@gouvfr/dsfr/dist/utility/utility.main.min.css'
import '@/main.css'
import { createRandomDbSetup, getRandomUser } from '@cpn-console/test-utils'
import type { ProjectV2 } from '@cpn-console/shared'
import { faker } from '@faker-js/faker'
import TeamCt from '@/components/TeamCt.vue'
import { useProjectStore } from '@/stores/project.js'
import { useUsersStore } from '@/stores/users.js'
import { useUserStore } from '@/stores/user.js'

const ownerId = faker.string.uuid()
const props: {
Expand Down Expand Up @@ -87,10 +87,11 @@ describe('TeamCt.vue', () => {
})
it('Should mount a TeamCt for user', () => {
useProjectStore()
const { project } = createRandomDbSetup({ nbUsers: 4 })
const newUser = getRandomUser()
const userStore = useUserStore()
// devrait tester que l'on peut toujours quitter un projet
userStore.userProfile = { id: props.project.members[0].id }

cy.intercept('GET', `api/v1/projects/${project.id}/users/match?letters=*`, { body: [newUser] })
// cy.intercept('GET', `api/v1/projects/${project.id}/users/match?letters=*`, { body: [newUser] })

cy.mount(TeamCt, { props: { ...props, canTransfer: false, canManage: false } })

Expand All @@ -101,7 +102,9 @@ describe('TeamCt.vue', () => {
cy.get('tbody > tr')
.should('have.length', props.project.members.length + 1) // +1 cause owner is not a member
cy.get('thead > tr > th')
.should('have.length', 3)
.should('have.length', 4)
// devrait tester que l'on peut toujours quitter un projet
// cy.get('.fr-fi-close-line').should('have.length', 1)
})
cy.getByDataTestid('showTransferProjectBtn')
.should('not.exist')
Expand Down
3 changes: 2 additions & 1 deletion apps/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
"pinia": "^2.1.7",
"vue": "^3.4.38",
"vue-router": "^4.4.5",
"vue3-json-viewer": "^2.2.2"
"vue3-json-viewer": "^2.2.2",
"xbytes": "^1.9.1"
},
"optionalDependencies": {
"@cypress/vue": "^6.0.1",
Expand Down
15 changes: 14 additions & 1 deletion apps/client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import { swaggerUiPath } from '@cpn-console/shared'
import { getKeycloak } from './utils/keycloak/keycloak.js'
import { useSnackbarStore } from './stores/snackbar.js'
import { useSystemSettingsStore } from './stores/system-settings.js'
import { useProjectStore } from './stores/project.js'
import { useUserStore } from './stores/user.js'
import { useServiceStore } from '@/stores/services-monitor.js'
const keycloak = getKeycloak()
const snackbarStore = useSnackbarStore()
const systemStore = useSystemSettingsStore()
const projectStore = useProjectStore()
const userStore = useUserStore()
const isLoggedIn = ref<boolean | undefined>(keycloak.authenticated)
Expand Down Expand Up @@ -35,6 +39,9 @@ const serviceStore = useServiceStore()
onBeforeMount(() => {
serviceStore.startHealthPolling()
serviceStore.checkServicesHealth()
if (userStore.isLoggedIn) {
projectStore.getMyProjects()
}
})
</script>

Expand All @@ -57,12 +64,14 @@ onBeforeMount(() => {
<router-view />
</div>
<DsoSnackbar />
<SelectProject
class="max-lg:hidden"
/>
</div>

<DsfrFooter
class="dso-footer"
a11y-compliance="partiellement conforme"
:logo-text="['Ministère', 'de l’Intérieur', 'et des Outre-Mer']"
:mandatory-links="[]"
>
<template #description>
Expand Down Expand Up @@ -92,4 +101,8 @@ onBeforeMount(() => {
.fr-container {
max-width: 100%;
}
.fr-header__logo {
display: none;
}
</style>
65 changes: 65 additions & 0 deletions apps/client/src/components/ConsumptionPanel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script lang="ts" setup>
import type { ProjectV2 } from '@cpn-console/shared'
import xbytes from 'xbytes'
import { computed, onBeforeMount } from 'vue'
import { useProjectStore } from '../stores/project.js'
import { type Consumption, listQuotasToConsumption } from '../utils/func.js'
import { useQuotaStore } from '../stores/quota.js'
const props = defineProps<{
ids: ProjectV2['id'][]
}>()
const projectStore = useProjectStore()
const quotaStore = useQuotaStore()
const consumption = computed<Consumption>(() => {
if (props.ids) {
const projects = props.ids.map(id => projectStore.myProjectsById[id] ?? projectStore.projectsById[id])
const quotas = projects.map(project => project.environments.map(env => quotaStore.quotasById[env.quotaId]))
.flat()
return listQuotasToConsumption(quotas)
}
return {
cpu: 0,
memory: 0,
}
})
onBeforeMount(() => {
quotaStore.getAllQuotas()
})
</script>

<template>
<div
id="consumption-panel"
data-testid="consumptionPanel"
class="grid grid-cols-1 content-between max-w-40 pl-2 p-1 place-items-end gap-2"
>
<DsfrBadge
v-if="props.ids.length > 1"
:label="`${props.ids.length} Projets`"
no-icon
type=""
/>
<DsfrBadge
v-if="consumption.cpu != null"
:label="`${consumption.cpu} CPU`"
no-icon
type=""
/>
<DsfrBadge
v-if="consumption.memory != null"
:label="`${xbytes(consumption.memory, { iec: true })} RAM`"
no-icon
type=""
/>
</div>
</template>

<style>
.fr-callout#consumption-panel {
margin: 0 !important;
margin-bottom: 0 !important;
}
</style>
8 changes: 7 additions & 1 deletion apps/client/src/components/LogsViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ async function showLogs(index: number) {
:step="step"
@set-page="showLogs($event)"
/>
<div
v-if="!logs.length"
class="text-center mt-2 w-full"
>
Aucun événement à afficher
</div>
<div
v-for="log in logs"
:key="log.id"
Expand All @@ -126,7 +132,7 @@ async function showLogs(index: number) {
class="flex gap-2"
>
<DsfrBadge
v-if="log?.data?.totalExecutionTime"
v-if="typeof log?.data?.totalExecutionTime !== 'undefined'"
:label="`${log.data.totalExecutionTime} ms`"
no-icon
/>
Expand Down
4 changes: 3 additions & 1 deletion apps/client/src/components/ProjectLogsViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ watch(projectStore, () => {
if (!projectStore.selectedProject) {
logStore.needRefresh = false
logStore.displayProjectLogs = true
return
}
logStore.needRefresh = true
})
</script>

Expand Down Expand Up @@ -87,7 +89,7 @@ watch(projectStore, () => {
:page="page"
:step="step"
header-class="grid-col-2 shrink"
body-class="grid-col-span-2 mt-1"
body-class="grid-col-span-2 mt-1 w-full"
mode="hide"
hide-total-events
pagination-position="top"
Expand Down
93 changes: 93 additions & 0 deletions apps/client/src/components/SelectProject.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<script setup lang="ts">
import router from '../router/index.js'
import { useUserStore } from '@/stores/user.js'
import { useProjectStore } from '@/stores/project.js'
import { useOrganizationStore } from '@/stores/organization.js'
const organizationStore = useOrganizationStore()
const projectStore = useProjectStore()
const userStore = useUserStore()
watch(userStore, () => {
if (userStore.isLoggedIn) {
projectStore.getMyProjects()
}
})
const projectOptions = computed(() => {
return organizationStore.organizations.map((org) => {
return {
org,
projects: projectStore.myProjects.filter(project => project.organizationId === org.id),
}
}).filter(org => org.projects.length)
})
function selectProject(id: string) {
projectStore.setSelectedProject(id)
router.push(`/projects/${id}/dashboard`)
}
</script>

<template>
<div
v-if="userStore.isLoggedIn"
class="select-project flex flex-row"
>
<select
v-if="projectStore.myProjects.length"
id="project-select"
class="fr-select"
@change="(e: any) => selectProject(e.target!.value)"
>
<template
v-for="group in projectOptions"
:key="group.org.id"
>
<optgroup
:label="group.org.label"
>
<option
v-for="project in group.projects"
:key="project.id"
:class="project.id === projectStore.selectedProject?.id ? 'bg-slate-500' : ''"
:value="project.id"
:selected="project.id === projectStore.selectedProject?.id"
>
{{ project.name }}
</option>
</optgroup>
</template>
</select>
<DsfrButton
:class="`create-project ${projectStore.myProjects.length ? 'w-15' : ''}`"
type="buttonType"
secondary
icon="ri:add-fill"
:icon-only="!!projectStore.myProjects.length"
:label="!projectStore.myProjects.length ? 'Créer un nouveau projet' : ''"
small
@click="() => router.currentRoute.value.name !== 'CreateProject' && router.push('/projects/create-project')"
/>
</div>
</template>
<style>
.select-project{
position: absolute;
padding-top: 1rem;
top: 0;
right: 15rem;
z-index: 1000;
}
.select-project select{
width: 250px;
height: 50px;
background-color: --var(--background-default-grey);
}
.select-project .create-project{
height: 50px;
}
</style>
Loading

0 comments on commit 8e3ac15

Please sign in to comment.