Skip to content
Open
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
45 changes: 33 additions & 12 deletions components/Metrics/AdminDataservicesPage.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
<template>
<div>
<div
v-if="downloadStatsUrl"
class="flex justify-end -mt-14 pt-0.5 mb-5"
>
<div class="flex justify-end items-center gap-4 -mt-14 pt-0.5 mb-5">
<AdminInput
v-model="q"
type="search"
:icon="RiSearchLine"
:placeholder="$t('Recherche')"
/>
<BrandedButton
v-if="downloadStatsUrl"
color="secondary"
:disabled="!downloadStatsUrl"
:href="downloadStatsUrl || ''"
:href="downloadStatsUrl"
:external="true"
download="stats.csv"
:icon="RiDownloadLine"
Expand Down Expand Up @@ -134,19 +137,34 @@
src="/illustrations/dataservice.svg"
class="h-20"
/>
<p class="fr-text--bold fr-my-3v">
{{ $t(`Vous n'avez pas encore publié d'API`) }}
</p>
<AdminPublishButton type="dataservice" />
<template v-if="q">
<p class="fr-text--bold fr-my-3v">
{{ $t(`Pas de résultats pour « {q} »`, { q }) }}
</p>
<BrandedButton
color="primary"
@click="q = qDebounced = ''"
>
{{ $t('Réinitialiser les filtres') }}
</BrandedButton>
</template>
<template v-else>
<p class="fr-text--bold fr-my-3v">
{{ $t(`Vous n'avez pas encore publié d'API`) }}
</p>
<AdminPublishButton type="dataservice" />
</template>
</div>
</div>
</template>

<script setup lang="ts">
import { BrandedButton, LoadingBlock, Pagination, summarize, Tooltip, type Dataservice, type Organization, type User } from '@datagouv/components-next'
import { RiChat3Line, RiDownloadLine, RiEyeLine, RiStarSLine } from '@remixicon/vue'
import { refDebounced } from '@vueuse/core'
import { RiChat3Line, RiDownloadLine, RiEyeLine, RiSearchLine, RiStarSLine } from '@remixicon/vue'
import AdminTable from '~/components/AdminTable/Table/AdminTable.vue'
import AdminTableTh from '~/components/AdminTable/Table/AdminTableTh.vue'
import AdminInput from '~/components/AdminInput.vue'
import type { DataserviceSortedBy, PaginatedArray, SortDirection } from '~/types/types'

const props = defineProps<{
Expand All @@ -158,6 +176,9 @@ const config = useRuntimeConfig()

const page = ref(1)
const pageSize = ref(20)
const q = ref('')
const qDebounced = refDebounced(q, config.public.searchDebounce)
watch(qDebounced, () => page.value = 1)
const sortedBy = ref<DataserviceSortedBy>('title')
const direction = ref<SortDirection>('desc')
const sortDirection = computed(() => `${direction.value === 'asc' ? '' : '-'}${sortedBy.value}`)
Expand All @@ -167,7 +188,7 @@ const params = computed(() => {
return {
organization: props.organization?.id,
owner: props.user?.id,

q: qDebounced.value,
sort: sortDirection.value,
page_size: pageSize.value,
page: page.value,
Expand Down
44 changes: 33 additions & 11 deletions components/Metrics/AdminDatasetsPage.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
<template>
<div>
<div
v-if="downloadStatsUrl"
class="flex justify-end -mt-14 pt-0.5 mb-5"
>
<div class="flex justify-end items-center gap-4 -mt-14 pt-0.5 mb-5">
<AdminInput
v-model="q"
type="search"
:icon="RiSearchLine"
:placeholder="$t('Recherche')"
/>
<BrandedButton
v-if="downloadStatsUrl"
color="secondary"
:disabled="!downloadStatsUrl"
:href="downloadStatsUrl || ''"
:href="downloadStatsUrl"
:external="true"
download="stats.csv"
:icon="RiDownloadLine"
Expand Down Expand Up @@ -172,19 +175,34 @@
src="/illustrations/dataset.svg"
class="h-20"
/>
<p class="fr-text--bold fr-my-3v">
{{ $t(`Vous n'avez pas encore publié de jeu de données.`) }}
</p>
<AdminPublishButton type="dataset" />
<template v-if="q">
<p class="fr-text--bold fr-my-3v">
{{ $t(`Pas de résultats pour « {q} »`, { q }) }}
</p>
<BrandedButton
color="primary"
@click="q = qDebounced = ''"
>
{{ $t('Réinitialiser les filtres') }}
</BrandedButton>
</template>
<template v-else>
<p class="fr-text--bold fr-my-3v">
{{ $t(`Vous n'avez pas encore publié de jeu de données.`) }}
</p>
<AdminPublishButton type="dataset" />
</template>
</div>
</div>
</template>

<script setup lang="ts">
import { BrandedButton, LoadingBlock, Pagination, summarize, Tooltip, type DatasetV2, type Organization, type User } from '@datagouv/components-next'
import { RiDownloadLine, RiEyeLine, RiLineChartLine, RiStarSLine } from '@remixicon/vue'
import { refDebounced } from '@vueuse/core'
import { RiDownloadLine, RiEyeLine, RiLineChartLine, RiSearchLine, RiStarSLine } from '@remixicon/vue'
import AdminTable from '~/components/AdminTable/Table/AdminTable.vue'
import AdminTableTh from '~/components/AdminTable/Table/AdminTableTh.vue'
import AdminInput from '~/components/AdminInput.vue'
import type { DatasetSortedBy, PaginatedArray, SortDirection } from '~/types/types'

const props = defineProps<{
Expand All @@ -195,6 +213,9 @@ const props = defineProps<{
const config = useRuntimeConfig()
const page = ref(1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

page should go back to 1 when qDebounced changes (for all 3 searches)

const pageSize = ref(20)
const q = ref('')
const qDebounced = refDebounced(q, config.public.searchDebounce)
watch(qDebounced, () => page.value = 1)

const sortedBy = ref<DatasetSortedBy>('created')
const direction = ref<SortDirection>('desc')
Expand All @@ -206,6 +227,7 @@ const params = computed(() => {
organization: props.organization?.id,
owner: props.user?.id,
user: props.user?.id,
q: qDebounced.value,
sort: sortDirection.value,
page_size: pageSize.value,
page: page.value,
Expand Down
45 changes: 33 additions & 12 deletions components/Metrics/AdminReusesPage.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
<template>
<div>
<div
v-if="downloadStatsUrl"
class="flex justify-end -mt-14 pt-0.5 mb-5"
>
<div class="flex justify-end items-center gap-4 -mt-14 pt-0.5 mb-5">
<AdminInput
v-model="q"
type="search"
:icon="RiSearchLine"
:placeholder="$t('Recherche')"
/>
<BrandedButton
v-if="downloadStatsUrl"
color="secondary"
:disabled="!downloadStatsUrl"
:href="downloadStatsUrl || ''"
:href="downloadStatsUrl"
:external="true"
download="stats.csv"
:icon="RiDownloadLine"
Expand Down Expand Up @@ -144,19 +147,34 @@
src="/illustrations/reuse.svg"
class="h-20"
/>
<p class="fr-text--bold fr-my-3v">
{{ $t(`Vous n'avez pas encore publié de réutilisation.`) }}
</p>
<AdminPublishButton type="reuse" />
<template v-if="q">
<p class="fr-text--bold fr-my-3v">
{{ $t(`Pas de résultats pour « {q} »`, { q }) }}
</p>
<BrandedButton
color="primary"
@click="q = qDebounced = ''"
>
{{ $t('Réinitialiser les filtres') }}
</BrandedButton>
</template>
<template v-else>
<p class="fr-text--bold fr-my-3v">
{{ $t(`Vous n'avez pas encore publié de réutilisation.`) }}
</p>
<AdminPublishButton type="reuse" />
</template>
</div>
</div>
</template>

<script setup lang="ts">
import { RiDownloadLine, RiStarSLine } from '@remixicon/vue'
import { RiDownloadLine, RiSearchLine, RiStarSLine } from '@remixicon/vue'
import { BrandedButton, LoadingBlock, Pagination, summarize, Tooltip, type Organization, type Reuse, type User } from '@datagouv/components-next'
import { refDebounced } from '@vueuse/core'
import AdminTable from '~/components/AdminTable/Table/AdminTable.vue'
import AdminTableTh from '~/components/AdminTable/Table/AdminTableTh.vue'
import AdminInput from '~/components/AdminInput.vue'
import type { PaginatedArray, ReuseSortedBy, SortDirection } from '~/types/types'

const props = defineProps<{
Expand All @@ -170,6 +188,9 @@ const downloadStatsUrl = computed(() => props.organization ? `/api/1/organizatio

const page = ref(1)
const pageSize = ref(20)
const q = ref('')
const qDebounced = refDebounced(q, config.public.searchDebounce)
watch(qDebounced, () => page.value = 1)
const sortedBy = ref<ReuseSortedBy>('created')
const direction = ref<SortDirection>('desc')
const sortDirection = computed(() => `${direction.value === 'asc' ? '' : '-'}${sortedBy.value}`)
Expand All @@ -178,7 +199,7 @@ const params = computed(() => {
return {
organization: props.organization?.id,
owner: props.user?.id,

q: qDebounced.value,
sort: sortDirection.value,
page_size: pageSize.value,
page: page.value,
Expand Down
75 changes: 75 additions & 0 deletions tests/admin/metrics-datasets.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { test, expect } from '@playwright/test'

const API_BASE = process.env.NUXT_PUBLIC_API_BASE || 'http://dev.local:7000'

test.describe('Dataset metrics', () => {
let datasetA: { id: string, title: string }
let datasetB: { id: string, title: string }
const uniqueId = Date.now()

test.beforeAll(async ({ browser }) => {
const context = await browser.newContext({ storageState: 'playwright/.auth/user.json' })
const page = await context.newPage()

const [respA, respB] = await Promise.all([
page.request.post(`${API_BASE}/api/1/datasets/`, {
data: { title: `Alpha metrics ${uniqueId}`, description: 'Test', frequency: 'unknown' },
}),
page.request.post(`${API_BASE}/api/1/datasets/`, {
data: { title: `Beta metrics ${uniqueId}`, description: 'Test', frequency: 'unknown' },
}),
])
datasetA = await respA.json()
datasetB = await respB.json()

await context.close()
})

test.afterAll(async ({ browser }) => {
const context = await browser.newContext({ storageState: 'playwright/.auth/user.json' })
const page = await context.newPage()

await Promise.all([
page.request.delete(`${API_BASE}/api/1/datasets/${datasetA.id}/`),
page.request.delete(`${API_BASE}/api/1/datasets/${datasetB.id}/`),
])

await context.close()
})

test('lists datasets with their metrics', async ({ page }) => {
await page.goto('/admin/me/metrics/datasets/')

await expect(page.getByRole('link', { name: datasetA.title })).toBeVisible()
await expect(page.getByRole('link', { name: datasetB.title })).toBeVisible()
})

test('can search datasets by title', async ({ page }) => {
await page.goto('/admin/me/metrics/datasets/')

const searchInput = page.getByRole('searchbox', { name: 'Recherche' })
await expect(searchInput).toBeVisible()

await searchInput.fill(`Alpha metrics ${uniqueId}`)
await expect(page.getByRole('link', { name: datasetA.title })).toBeVisible()
await expect(page.getByRole('link', { name: datasetB.title })).not.toBeVisible()

await searchInput.clear()
await expect(page.getByRole('link', { name: datasetA.title })).toBeVisible()
await expect(page.getByRole('link', { name: datasetB.title })).toBeVisible()
})

test('shows empty state and reset button when search has no results', async ({ page }) => {
await page.goto('/admin/me/metrics/datasets/')

const searchInput = page.getByRole('searchbox', { name: 'Recherche' })
await searchInput.fill('zzz-no-match-zzz')

await expect(page.getByText('Pas de résultats pour « zzz-no-match-zzz »')).toBeVisible()

await page.getByRole('button', { name: 'Réinitialiser les filtres' }).click()

await expect(searchInput).toHaveValue('')
await expect(page.getByRole('link', { name: datasetA.title })).toBeVisible()
})
})
Loading