@sensolus/create-snt-agent-app 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/package.json +1 -1
  2. package/template/CLAUDE.md +218 -0
  3. package/template/Dockerfile +32 -0
  4. package/template/Jenkinsfile +28 -0
  5. package/template/README.md +477 -16
  6. package/template/_env.example +4 -0
  7. package/template/backend/app.py +630 -49
  8. package/template/backend/db_config.py +16 -0
  9. package/template/backend/extensions.py +3 -0
  10. package/template/backend/init_db.py +75 -0
  11. package/template/backend/migrations/README +1 -0
  12. package/template/backend/migrations/alembic.ini +50 -0
  13. package/template/backend/migrations/env.py +113 -0
  14. package/template/backend/migrations/script.py.mako +24 -0
  15. package/template/backend/migrations/versions/001_add_favourite_organisations.py +31 -0
  16. package/template/backend/migrations/versions/002_add_org_daily_stats.py +36 -0
  17. package/template/backend/models.py +31 -0
  18. package/template/backend/requirements.txt +8 -2
  19. package/template/eslint.config.js +6 -2
  20. package/template/index.html +11 -8
  21. package/template/infra/docker-compose.yml +15 -0
  22. package/template/openapi.json +40357 -0
  23. package/template/package.json +8 -1
  24. package/template/scripts/create-ecr-repo.sh +42 -0
  25. package/template/src/App.jsx +12 -34
  26. package/template/src/hooks/useFavourites.js +44 -0
  27. package/template/src/i18n/index.js +3 -0
  28. package/template/src/i18n/messages.js +8 -14
  29. package/template/src/i18n/translations/de.js +96 -0
  30. package/template/src/i18n/translations/en.js +103 -0
  31. package/template/src/i18n/translations/es.js +96 -0
  32. package/template/src/i18n/translations/fr.js +96 -0
  33. package/template/src/i18n/translations/nl.js +96 -0
  34. package/template/src/main.jsx +2 -3
  35. package/template/src/pages/Home.jsx +170 -0
  36. package/template/src/pages/OrganisationDetail.jsx +259 -0
  37. package/template/src/pages/OrganisationList.jsx +457 -0
  38. package/template/src/pages/Overview.jsx +199 -0
  39. package/template/src/pages/WidgetShowcase.jsx +522 -0
  40. package/template/src/styles/app.css +543 -4
  41. package/template/start-backend.sh +4 -0
  42. package/template/start-frontend.sh +3 -0
@@ -2,6 +2,7 @@
2
2
  "name": "{{APP_NAME}}",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
+ "description": "Sensolus agent app (generated by @sensolus/create-snt-agent-app).",
5
6
  "type": "module",
6
7
  "scripts": {
7
8
  "dev": "vite",
@@ -9,8 +10,12 @@
9
10
  "preview": "vite preview",
10
11
  "lint": "eslint src"
11
12
  },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+ssh://git@bitbucket.org/sensolus/{{APP_NAME}}.git"
16
+ },
12
17
  "dependencies": {
13
- "@sensolus/snt-agent-kit": "^0.1.0",
18
+ "@sensolus/snt-agent-kit": "^0.1.1",
14
19
  "react": "^19.2.3",
15
20
  "react-dom": "^19.2.3",
16
21
  "react-router-dom": "^7.1.1"
@@ -20,6 +25,8 @@
20
25
  "@vitejs/plugin-react": "^5.1.2",
21
26
  "eslint": "^9.18.0",
22
27
  "eslint-plugin-react": "^7.37.0",
28
+ "eslint-plugin-react-hooks": "^5.1.0",
29
+ "globals": "^15.14.0",
23
30
  "vite": "^7.3.1"
24
31
  }
25
32
  }
@@ -0,0 +1,42 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ if [ -z "$1" ]; then
5
+ echo "Usage: $0 <repository-name>"
6
+ echo "Example: $0 {{APP_NAME}}"
7
+ exit 1
8
+ fi
9
+
10
+ REPO_NAME="$1"
11
+ REGION="${AWS_REGION:-eu-west-1}"
12
+
13
+ echo "Creating ECR repository: $REPO_NAME in $REGION"
14
+
15
+ aws ecr create-repository \
16
+ --repository-name "$REPO_NAME" \
17
+ --region "$REGION" \
18
+ --image-scanning-configuration scanOnPush=true
19
+
20
+ echo "Setting lifecycle policy to retain only 3 images"
21
+
22
+ aws ecr put-lifecycle-policy \
23
+ --repository-name "$REPO_NAME" \
24
+ --region "$REGION" \
25
+ --lifecycle-policy-text '{
26
+ "rules": [
27
+ {
28
+ "rulePriority": 1,
29
+ "description": "Keep only 3 images",
30
+ "selection": {
31
+ "tagStatus": "any",
32
+ "countType": "imageCountMoreThan",
33
+ "countNumber": 3
34
+ },
35
+ "action": {
36
+ "type": "expire"
37
+ }
38
+ }
39
+ ]
40
+ }'
41
+
42
+ echo "Done. Repository URL: $(aws ecr describe-repositories --repository-names "$REPO_NAME" --region "$REGION" --query 'repositories[0].repositoryUri' --output text)"
@@ -1,38 +1,16 @@
1
- import { useEffect, useState } from 'react'
2
- import { SntPageHeader, SntTable, SntBadge, SntButton, SntToolbar, SntLoadingOverlay, useLocale } from '@sensolus/snt-agent-kit'
3
-
4
- export default function App() {
5
- const { t } = useLocale()
6
- const [orgs, setOrgs] = useState(null)
7
- const [loading, setLoading] = useState(false)
8
-
9
- async function load() {
10
- setLoading(true)
11
- try {
12
- const res = await fetch('/api/organisations')
13
- setOrgs(res.ok ? await res.json() : [])
14
- } finally {
15
- setLoading(false)
16
- }
17
- }
18
-
19
- useEffect(() => { load() }, [])
20
-
21
- if (loading || orgs === null) return <SntLoadingOverlay message={t('common.loading')} />
1
+ import { BrowserRouter, Routes, Route } from 'react-router-dom'
2
+ import { Home } from './pages/Home'
3
+ import { OrganisationDetail } from './pages/OrganisationDetail'
22
4
 
5
+ function App() {
23
6
  return (
24
- <div className="app-container">
25
- <SntPageHeader title={t('app.title')} />
26
- <SntToolbar>
27
- <SntButton variant="secondary" onClick={load}>{t('common.reload')}</SntButton>
28
- </SntToolbar>
29
- <SntTable
30
- data={orgs}
31
- columns={[
32
- { key: 'name', header: t('app.org.name') },
33
- { key: 'status', header: t('app.org.status'), render: (row, val) => <SntBadge variant={val === 'ACTIVE' ? 'success' : 'secondary'} text={String(val ?? '')} /> },
34
- ]}
35
- />
36
- </div>
7
+ <BrowserRouter>
8
+ <Routes>
9
+ <Route path="/" element={<Home />} />
10
+ <Route path="/organisation/:id" element={<OrganisationDetail />} />
11
+ </Routes>
12
+ </BrowserRouter>
37
13
  )
38
14
  }
15
+
16
+ export default App
@@ -0,0 +1,44 @@
1
+ import { useState, useEffect, useCallback } from 'react'
2
+
3
+ export function useFavourites() {
4
+ const [favourites, setFavourites] = useState(new Set())
5
+ const [loading, setLoading] = useState(true)
6
+
7
+ useEffect(() => {
8
+ fetch('/api/favourites')
9
+ .then(res => res.json())
10
+ .then(ids => setFavourites(new Set(ids)))
11
+ .catch(() => setFavourites(new Set()))
12
+ .finally(() => setLoading(false))
13
+ }, [])
14
+
15
+ const toggleFavourite = useCallback(async (orgId) => {
16
+ const isFav = favourites.has(orgId)
17
+ // Optimistic update
18
+ setFavourites(prev => {
19
+ const next = new Set(prev)
20
+ if (isFav) next.delete(orgId)
21
+ else next.add(orgId)
22
+ return next
23
+ })
24
+
25
+ try {
26
+ const res = await fetch(`/api/favourites/${orgId}`, {
27
+ method: isFav ? 'DELETE' : 'PUT',
28
+ })
29
+ if (!res.ok) throw new Error()
30
+ } catch {
31
+ // Revert on error
32
+ setFavourites(prev => {
33
+ const next = new Set(prev)
34
+ if (isFav) next.add(orgId)
35
+ else next.delete(orgId)
36
+ return next
37
+ })
38
+ }
39
+ }, [favourites])
40
+
41
+ const isFavourite = useCallback((orgId) => favourites.has(orgId), [favourites])
42
+
43
+ return { favourites, toggleFavourite, isFavourite, loading }
44
+ }
@@ -0,0 +1,3 @@
1
+ // i18n framework comes from the kit; this app owns only its translation keys.
2
+ export { LocaleProvider, useLocale, formatShortDate, formatDateTime, formatNumber } from '@sensolus/snt-agent-kit'
3
+ export { messages } from './messages'
@@ -1,17 +1,11 @@
1
1
  /**
2
2
  * App-owned translation keys, merged into the kit's common strings via
3
- * <LocaleProvider messages={messages}>. Add languages/keys as needed;
4
- * English is the fallback for missing keys.
3
+ * <LocaleProvider messages={messages}> (common.* / table.* ship with the kit).
5
4
  */
6
- export const messages = {
7
- en: {
8
- 'app.title': '{{APP_NAME}}',
9
- 'app.org.name': 'Name',
10
- 'app.org.status': 'Status',
11
- },
12
- nl: {
13
- 'app.title': '{{APP_NAME}}',
14
- 'app.org.name': 'Naam',
15
- 'app.org.status': 'Status',
16
- },
17
- }
5
+ import en from './translations/en'
6
+ import nl from './translations/nl'
7
+ import fr from './translations/fr'
8
+ import de from './translations/de'
9
+ import es from './translations/es'
10
+
11
+ export const messages = { en, nl, fr, de, es }
@@ -0,0 +1,96 @@
1
+ export default {
2
+ // Home / tabs
3
+ 'home.tab.overview': 'Übersicht',
4
+ 'home.tab.explore': 'Erkunden',
5
+ 'home.tab.showcase': 'Widgets',
6
+
7
+ // Overview
8
+ 'overview.title': 'Übersicht',
9
+ 'overview.empty': 'Noch keine Snapshots erfasst. Der tägliche Snapshot füllt dieses Diagramm.',
10
+ 'overview.chart.title': 'Tageswerte über alle Organisationen',
11
+ 'overview.series.orgs': 'Organisationen',
12
+ 'overview.series.trackers': 'Tracker',
13
+ 'overview.series.users': 'Benutzer',
14
+
15
+ // Common
16
+
17
+ // Table widget
18
+
19
+ // CheckboxList widget
20
+ 'checkboxList.selectAll': 'Alle auswählen',
21
+ 'checkboxList.deselectAll': 'Alle abwählen',
22
+
23
+ // Sidepanel
24
+ 'sidepanel.collapse': 'Panel einklappen',
25
+ 'sidepanel.expand': 'Panel ausklappen',
26
+
27
+ // DateRangePicker presets
28
+ 'dateRange.period': 'Zeitraum:',
29
+ 'dateRange.from': 'Von',
30
+ 'dateRange.to': 'Bis',
31
+ 'dateRange.preset.this_month': 'Dieser Monat',
32
+ 'dateRange.preset.last_month': 'Letzter Monat',
33
+ 'dateRange.preset.last_3_months': 'Letzte 3 Monate',
34
+ 'dateRange.preset.last_6_months': 'Letzte 6 Monate',
35
+ 'dateRange.preset.last_12_months': 'Letzte 12 Monate',
36
+ 'dateRange.preset.last_24_months': 'Letzte 24 Monate',
37
+ 'dateRange.preset.last_36_months': 'Letzte 36 Monate',
38
+ 'dateRange.preset.this_year': 'Dieses Jahr',
39
+ 'dateRange.preset.last_year': 'Letztes Jahr',
40
+ 'dateRange.preset.all_time': 'Gesamter Zeitraum',
41
+ 'dateRange.preset.custom': 'Benutzerdefiniert',
42
+ 'dateRange.preset.next_3_months': 'Nächste 3 Monate',
43
+ 'dateRange.preset.next_6_months': 'Nächste 6 Monate',
44
+ 'dateRange.preset.next_12_months': 'Nächste 12 Monate',
45
+ 'dateRange.preset.this_quarter': 'Dieses Quartal',
46
+ 'dateRange.preset.next_quarter': 'Nächstes Quartal',
47
+ 'dateRange.preset.rolling_12_months': 'Rollende 12 Monate',
48
+
49
+ // OrganisationList page
50
+ 'orgList.title': 'Organisationen',
51
+ 'orgList.apiKeyPlaceholder': 'API-Schlüssel...',
52
+ 'orgList.searchPlaceholder': 'Nach Name suchen...',
53
+ 'orgList.enterApiKey': 'Bitte geben Sie einen API-Schlüssel ein',
54
+ 'orgList.fetchFailed': 'Organisationen konnten nicht geladen werden',
55
+ 'orgList.loadingOrgs': 'Organisationen werden geladen...',
56
+ 'orgList.showing': (shown, total) => `${shown} von ${total} Organisationen`,
57
+ 'orgList.noOrgsFound': 'Keine Organisationen gefunden',
58
+ 'orgList.noOrgsMatch': 'Keine Organisationen entsprechen den aktuellen Filtern.',
59
+ 'orgList.activeSubscriptions': 'Aktive Abonnements',
60
+ 'orgList.onlyActive': 'Nur mit aktiven Abonnements',
61
+ 'orgList.minSubscriptions': (n) => `Min. Abonnements: ${n}`,
62
+ 'orgList.organisationType': 'Organisationstyp',
63
+ 'orgList.favourites': 'Favoriten',
64
+ 'orgList.onlyFavourites': 'Nur Favoriten',
65
+ 'orgList.addFavourite': 'Zu Favoriten hinzufügen',
66
+ 'orgList.removeFavourite': 'Aus Favoriten entfernen',
67
+ 'orgList.summary.favourites': 'Favoriten',
68
+ 'orgList.summary.organisations': 'Organisationen',
69
+ 'orgList.summary.totalTrackers': 'Tracker gesamt',
70
+ 'orgList.summary.activeSubscriptions': 'Aktive Abonnements',
71
+ 'orgList.summary.totalUsers': 'Benutzer gesamt',
72
+ 'orgList.summary.avgOnlineRatio': 'Durchschn. Online-Rate',
73
+ 'orgList.col.name': 'Name',
74
+ 'orgList.col.id': 'ID',
75
+ 'orgList.col.type': 'Typ',
76
+ 'orgList.col.trackers': 'Tracker',
77
+ 'orgList.col.activeSubs': 'Aktive Abon.',
78
+ 'orgList.col.users': 'Benutzer',
79
+ 'orgList.col.online': 'Online',
80
+ 'orgList.stat.id': 'ID',
81
+ 'orgList.stat.trackers': 'Tracker',
82
+ 'orgList.stat.activeSubscriptions': 'Aktive Abonnements',
83
+ 'orgList.stat.users': 'Benutzer',
84
+ 'orgList.stat.online': 'Online',
85
+
86
+ // OrganisationDetail page
87
+ 'orgDetail.notFound': 'Organisation nicht gefunden',
88
+ 'orgDetail.notAvailable': 'Organisationsdaten nicht verfügbar',
89
+ 'orgDetail.goBackPrompt': 'Bitte gehen Sie zurück zur Liste und wählen Sie eine Organisation.',
90
+ 'orgDetail.basicInfo': 'Allgemeine Informationen',
91
+ 'orgDetail.metrics': 'Metriken',
92
+ 'orgDetail.statistics': 'Statistiken',
93
+ 'orgDetail.otherDetails': 'Weitere Details',
94
+ 'orgDetail.locked': 'Gesperrt',
95
+ 'orgDetail.active': 'Aktiv',
96
+ }
@@ -0,0 +1,103 @@
1
+ export default {
2
+ // Home / tabs
3
+ 'home.tab.overview': 'Overview',
4
+ 'home.tab.explore': 'Explore',
5
+ 'home.tab.showcase': 'Widgets',
6
+
7
+ // Overview
8
+ 'overview.title': 'Overview',
9
+ 'overview.empty': 'No snapshots collected yet. The daily snapshot will populate this chart.',
10
+ 'overview.chart.title': 'Daily totals across organisations',
11
+ 'overview.series.orgs': 'Organisations',
12
+ 'overview.series.trackers': 'Trackers',
13
+ 'overview.series.users': 'Users',
14
+
15
+ // Common
16
+
17
+ // Table widget
18
+
19
+ // CheckboxList widget
20
+ 'checkboxList.selectAll': 'Select all',
21
+ 'checkboxList.deselectAll': 'Deselect all',
22
+
23
+ // Sidepanel
24
+ 'sidepanel.collapse': 'Collapse panel',
25
+ 'sidepanel.expand': 'Expand panel',
26
+
27
+ // DateRangePicker presets
28
+ 'dateRange.period': 'Period:',
29
+ 'dateRange.from': 'From',
30
+ 'dateRange.to': 'To',
31
+ 'dateRange.preset.this_month': 'This month',
32
+ 'dateRange.preset.last_month': 'Last month',
33
+ 'dateRange.preset.last_3_months': 'Last 3 months',
34
+ 'dateRange.preset.last_6_months': 'Last 6 months',
35
+ 'dateRange.preset.last_12_months': 'Last 12 months',
36
+ 'dateRange.preset.last_24_months': 'Last 24 months',
37
+ 'dateRange.preset.last_36_months': 'Last 36 months',
38
+ 'dateRange.preset.this_year': 'This year',
39
+ 'dateRange.preset.last_year': 'Last year',
40
+ 'dateRange.preset.all_time': 'All time',
41
+ 'dateRange.preset.custom': 'Custom',
42
+ 'dateRange.preset.next_3_months': 'Next 3 months',
43
+ 'dateRange.preset.next_6_months': 'Next 6 months',
44
+ 'dateRange.preset.next_12_months': 'Next 12 months',
45
+ 'dateRange.preset.this_quarter': 'This quarter',
46
+ 'dateRange.preset.next_quarter': 'Next quarter',
47
+ 'dateRange.preset.rolling_12_months': 'Rolling 12 months',
48
+
49
+ // Auth dialog
50
+ 'auth.dialog.title': 'Sensolus API key required',
51
+ 'auth.dialog.description':
52
+ 'No active session was found. Enter a Sensolus API key to load data. The key is stored for this browser session and shared across all tabs.',
53
+ 'auth.dialog.failed': 'Failed to save API key',
54
+
55
+ // OrganisationList page
56
+ 'orgList.title': 'Organisations',
57
+ 'orgList.apiKeyPlaceholder': 'API key...',
58
+ 'orgList.changeApiKey': 'Change API key',
59
+ 'orgList.searchPlaceholder': 'Search by name...',
60
+ 'orgList.enterApiKey': 'Please enter an API key',
61
+ 'orgList.fetchFailed': 'Failed to fetch organisations',
62
+ 'orgList.loadingOrgs': 'Loading organisations...',
63
+ 'orgList.showing': (shown, total) => `Showing ${shown} of ${total} organisations`,
64
+ 'orgList.noOrgsFound': 'No organisations found',
65
+ 'orgList.noOrgsMatch': 'No organisations match your current filters.',
66
+ 'orgList.activeSubscriptions': 'Active Subscriptions',
67
+ 'orgList.onlyActive': 'Only with active subscriptions',
68
+ 'orgList.minSubscriptions': (n) => `Min. Subscriptions: ${n}`,
69
+ 'orgList.organisationType': 'Organisation Type',
70
+ 'orgList.favourites': 'Favourites',
71
+ 'orgList.onlyFavourites': 'Only favourites',
72
+ 'orgList.addFavourite': 'Add to favourites',
73
+ 'orgList.removeFavourite': 'Remove from favourites',
74
+ 'orgList.summary.favourites': 'Favourites',
75
+ 'orgList.summary.organisations': 'Organisations',
76
+ 'orgList.summary.totalTrackers': 'Total Trackers',
77
+ 'orgList.summary.activeSubscriptions': 'Active Subscriptions',
78
+ 'orgList.summary.totalUsers': 'Total Users',
79
+ 'orgList.summary.avgOnlineRatio': 'Avg Online Ratio',
80
+ 'orgList.col.name': 'Name',
81
+ 'orgList.col.id': 'ID',
82
+ 'orgList.col.type': 'Type',
83
+ 'orgList.col.trackers': 'Trackers',
84
+ 'orgList.col.activeSubs': 'Active Subs',
85
+ 'orgList.col.users': 'Users',
86
+ 'orgList.col.online': 'Online',
87
+ 'orgList.stat.id': 'ID',
88
+ 'orgList.stat.trackers': 'Trackers',
89
+ 'orgList.stat.activeSubscriptions': 'Active Subscriptions',
90
+ 'orgList.stat.users': 'Users',
91
+ 'orgList.stat.online': 'Online',
92
+
93
+ // OrganisationDetail page
94
+ 'orgDetail.notFound': 'Organisation Not Found',
95
+ 'orgDetail.notAvailable': 'Organisation data not available',
96
+ 'orgDetail.goBackPrompt': 'Please go back to the list and select an organisation.',
97
+ 'orgDetail.basicInfo': 'Basic Information',
98
+ 'orgDetail.metrics': 'Metrics',
99
+ 'orgDetail.statistics': 'Statistics',
100
+ 'orgDetail.otherDetails': 'Other Details',
101
+ 'orgDetail.locked': 'Locked',
102
+ 'orgDetail.active': 'Active',
103
+ }
@@ -0,0 +1,96 @@
1
+ export default {
2
+ // Home / tabs
3
+ 'home.tab.overview': 'Resumen',
4
+ 'home.tab.explore': 'Explorar',
5
+ 'home.tab.showcase': 'Widgets',
6
+
7
+ // Overview
8
+ 'overview.title': 'Resumen',
9
+ 'overview.empty': 'Aún no se han recopilado capturas. La captura diaria llenará este gráfico.',
10
+ 'overview.chart.title': 'Totales diarios por organización',
11
+ 'overview.series.orgs': 'Organizaciones',
12
+ 'overview.series.trackers': 'Rastreadores',
13
+ 'overview.series.users': 'Usuarios',
14
+
15
+ // Common
16
+
17
+ // Table widget
18
+
19
+ // CheckboxList widget
20
+ 'checkboxList.selectAll': 'Seleccionar todo',
21
+ 'checkboxList.deselectAll': 'Deseleccionar todo',
22
+
23
+ // Sidepanel
24
+ 'sidepanel.collapse': 'Contraer panel',
25
+ 'sidepanel.expand': 'Expandir panel',
26
+
27
+ // DateRangePicker presets
28
+ 'dateRange.period': 'Período:',
29
+ 'dateRange.from': 'Desde',
30
+ 'dateRange.to': 'Hasta',
31
+ 'dateRange.preset.this_month': 'Este mes',
32
+ 'dateRange.preset.last_month': 'Mes pasado',
33
+ 'dateRange.preset.last_3_months': 'Últimos 3 meses',
34
+ 'dateRange.preset.last_6_months': 'Últimos 6 meses',
35
+ 'dateRange.preset.last_12_months': 'Últimos 12 meses',
36
+ 'dateRange.preset.last_24_months': 'Últimos 24 meses',
37
+ 'dateRange.preset.last_36_months': 'Últimos 36 meses',
38
+ 'dateRange.preset.this_year': 'Este año',
39
+ 'dateRange.preset.last_year': 'Año pasado',
40
+ 'dateRange.preset.all_time': 'Todo el período',
41
+ 'dateRange.preset.custom': 'Personalizado',
42
+ 'dateRange.preset.next_3_months': 'Próximos 3 meses',
43
+ 'dateRange.preset.next_6_months': 'Próximos 6 meses',
44
+ 'dateRange.preset.next_12_months': 'Próximos 12 meses',
45
+ 'dateRange.preset.this_quarter': 'Este trimestre',
46
+ 'dateRange.preset.next_quarter': 'Próximo trimestre',
47
+ 'dateRange.preset.rolling_12_months': '12 meses continuos',
48
+
49
+ // OrganisationList page
50
+ 'orgList.title': 'Organizaciones',
51
+ 'orgList.apiKeyPlaceholder': 'Clave API...',
52
+ 'orgList.searchPlaceholder': 'Buscar por nombre...',
53
+ 'orgList.enterApiKey': 'Por favor, introduzca una clave API',
54
+ 'orgList.fetchFailed': 'Error al cargar las organizaciones',
55
+ 'orgList.loadingOrgs': 'Cargando organizaciones...',
56
+ 'orgList.showing': (shown, total) => `Mostrando ${shown} de ${total} organizaciones`,
57
+ 'orgList.noOrgsFound': 'No se encontraron organizaciones',
58
+ 'orgList.noOrgsMatch': 'Ninguna organización coincide con los filtros actuales.',
59
+ 'orgList.activeSubscriptions': 'Suscripciones activas',
60
+ 'orgList.onlyActive': 'Solo con suscripciones activas',
61
+ 'orgList.minSubscriptions': (n) => `Min. suscripciones: ${n}`,
62
+ 'orgList.organisationType': 'Tipo de organización',
63
+ 'orgList.favourites': 'Favoritos',
64
+ 'orgList.onlyFavourites': 'Solo favoritos',
65
+ 'orgList.addFavourite': 'Agregar a favoritos',
66
+ 'orgList.removeFavourite': 'Eliminar de favoritos',
67
+ 'orgList.summary.favourites': 'Favoritos',
68
+ 'orgList.summary.organisations': 'Organizaciones',
69
+ 'orgList.summary.totalTrackers': 'Total trackers',
70
+ 'orgList.summary.activeSubscriptions': 'Suscripciones activas',
71
+ 'orgList.summary.totalUsers': 'Total usuarios',
72
+ 'orgList.summary.avgOnlineRatio': 'Ratio en línea prom.',
73
+ 'orgList.col.name': 'Nombre',
74
+ 'orgList.col.id': 'ID',
75
+ 'orgList.col.type': 'Tipo',
76
+ 'orgList.col.trackers': 'Trackers',
77
+ 'orgList.col.activeSubs': 'Susc. activas',
78
+ 'orgList.col.users': 'Usuarios',
79
+ 'orgList.col.online': 'En línea',
80
+ 'orgList.stat.id': 'ID',
81
+ 'orgList.stat.trackers': 'Trackers',
82
+ 'orgList.stat.activeSubscriptions': 'Suscripciones activas',
83
+ 'orgList.stat.users': 'Usuarios',
84
+ 'orgList.stat.online': 'En línea',
85
+
86
+ // OrganisationDetail page
87
+ 'orgDetail.notFound': 'Organización no encontrada',
88
+ 'orgDetail.notAvailable': 'Datos de la organización no disponibles',
89
+ 'orgDetail.goBackPrompt': 'Vuelva a la lista y seleccione una organización.',
90
+ 'orgDetail.basicInfo': 'Información general',
91
+ 'orgDetail.metrics': 'Métricas',
92
+ 'orgDetail.statistics': 'Estadísticas',
93
+ 'orgDetail.otherDetails': 'Otros detalles',
94
+ 'orgDetail.locked': 'Bloqueado',
95
+ 'orgDetail.active': 'Activo',
96
+ }
@@ -0,0 +1,96 @@
1
+ export default {
2
+ // Home / tabs
3
+ 'home.tab.overview': 'Vue d\'ensemble',
4
+ 'home.tab.explore': 'Explorer',
5
+ 'home.tab.showcase': 'Widgets',
6
+
7
+ // Overview
8
+ 'overview.title': 'Vue d\'ensemble',
9
+ 'overview.empty': 'Aucun instantané collecté pour l\'instant. L\'instantané quotidien remplira ce graphique.',
10
+ 'overview.chart.title': 'Totaux journaliers par organisation',
11
+ 'overview.series.orgs': 'Organisations',
12
+ 'overview.series.trackers': 'Traceurs',
13
+ 'overview.series.users': 'Utilisateurs',
14
+
15
+ // Common
16
+
17
+ // Table widget
18
+
19
+ // CheckboxList widget
20
+ 'checkboxList.selectAll': 'Tout sélectionner',
21
+ 'checkboxList.deselectAll': 'Tout désélectionner',
22
+
23
+ // Sidepanel
24
+ 'sidepanel.collapse': 'Réduire le panneau',
25
+ 'sidepanel.expand': 'Développer le panneau',
26
+
27
+ // DateRangePicker presets
28
+ 'dateRange.period': 'Période :',
29
+ 'dateRange.from': 'Du',
30
+ 'dateRange.to': 'Au',
31
+ 'dateRange.preset.this_month': 'Ce mois-ci',
32
+ 'dateRange.preset.last_month': 'Le mois dernier',
33
+ 'dateRange.preset.last_3_months': '3 derniers mois',
34
+ 'dateRange.preset.last_6_months': '6 derniers mois',
35
+ 'dateRange.preset.last_12_months': '12 derniers mois',
36
+ 'dateRange.preset.last_24_months': '24 derniers mois',
37
+ 'dateRange.preset.last_36_months': '36 derniers mois',
38
+ 'dateRange.preset.this_year': 'Cette année',
39
+ 'dateRange.preset.last_year': 'L\'année dernière',
40
+ 'dateRange.preset.all_time': 'Tout',
41
+ 'dateRange.preset.custom': 'Personnalisé',
42
+ 'dateRange.preset.next_3_months': '3 prochains mois',
43
+ 'dateRange.preset.next_6_months': '6 prochains mois',
44
+ 'dateRange.preset.next_12_months': '12 prochains mois',
45
+ 'dateRange.preset.this_quarter': 'Ce trimestre',
46
+ 'dateRange.preset.next_quarter': 'Trimestre prochain',
47
+ 'dateRange.preset.rolling_12_months': '12 mois glissants',
48
+
49
+ // OrganisationList page
50
+ 'orgList.title': 'Organisations',
51
+ 'orgList.apiKeyPlaceholder': 'Clé API...',
52
+ 'orgList.searchPlaceholder': 'Rechercher par nom...',
53
+ 'orgList.enterApiKey': 'Veuillez entrer une clé API',
54
+ 'orgList.fetchFailed': 'Échec du chargement des organisations',
55
+ 'orgList.loadingOrgs': 'Chargement des organisations...',
56
+ 'orgList.showing': (shown, total) => `${shown} sur ${total} organisations`,
57
+ 'orgList.noOrgsFound': 'Aucune organisation trouvée',
58
+ 'orgList.noOrgsMatch': 'Aucune organisation ne correspond aux filtres actuels.',
59
+ 'orgList.activeSubscriptions': 'Abonnements actifs',
60
+ 'orgList.onlyActive': 'Uniquement avec abonnements actifs',
61
+ 'orgList.minSubscriptions': (n) => `Min. abonnements : ${n}`,
62
+ 'orgList.organisationType': 'Type d\'organisation',
63
+ 'orgList.favourites': 'Favoris',
64
+ 'orgList.onlyFavourites': 'Uniquement les favoris',
65
+ 'orgList.addFavourite': 'Ajouter aux favoris',
66
+ 'orgList.removeFavourite': 'Retirer des favoris',
67
+ 'orgList.summary.favourites': 'Favoris',
68
+ 'orgList.summary.organisations': 'Organisations',
69
+ 'orgList.summary.totalTrackers': 'Total trackers',
70
+ 'orgList.summary.activeSubscriptions': 'Abonnements actifs',
71
+ 'orgList.summary.totalUsers': 'Total utilisateurs',
72
+ 'orgList.summary.avgOnlineRatio': 'Ratio en ligne moy.',
73
+ 'orgList.col.name': 'Nom',
74
+ 'orgList.col.id': 'ID',
75
+ 'orgList.col.type': 'Type',
76
+ 'orgList.col.trackers': 'Trackers',
77
+ 'orgList.col.activeSubs': 'Abon. actifs',
78
+ 'orgList.col.users': 'Utilisateurs',
79
+ 'orgList.col.online': 'En ligne',
80
+ 'orgList.stat.id': 'ID',
81
+ 'orgList.stat.trackers': 'Trackers',
82
+ 'orgList.stat.activeSubscriptions': 'Abonnements actifs',
83
+ 'orgList.stat.users': 'Utilisateurs',
84
+ 'orgList.stat.online': 'En ligne',
85
+
86
+ // OrganisationDetail page
87
+ 'orgDetail.notFound': 'Organisation non trouvée',
88
+ 'orgDetail.notAvailable': 'Données de l\'organisation non disponibles',
89
+ 'orgDetail.goBackPrompt': 'Veuillez retourner à la liste et sélectionner une organisation.',
90
+ 'orgDetail.basicInfo': 'Informations générales',
91
+ 'orgDetail.metrics': 'Métriques',
92
+ 'orgDetail.statistics': 'Statistiques',
93
+ 'orgDetail.otherDetails': 'Autres détails',
94
+ 'orgDetail.locked': 'Verrouillé',
95
+ 'orgDetail.active': 'Actif',
96
+ }