Compare commits

...

5 commits

Author SHA1 Message Date
d01d197bde breadcrumbs arreglar en mobile 2023-12-09 01:04:28 -03:00
9dd6ab32bf actualizar lockfile 2023-12-09 00:47:04 -03:00
0ee2b5993e modo oscuro + organización de código 2023-12-08 21:17:25 -03:00
c9347b583b frontend: generar titulo de página 2023-12-08 20:39:16 -03:00
2ac17883a9 mover fondo a index.html 2023-12-08 20:30:19 -03:00
14 changed files with 179 additions and 172 deletions

View file

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Transicion Desordenada Diablo (ver dataset)</title>
</head>
<body>
<body class="bg-gray-50 dark:bg-gray-900 dark:text-white">
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>

View file

@ -10,6 +10,7 @@
"check": "svelte-check --tsconfig ./tsconfig.json"
},
"devDependencies": {
"@poppanator/sveltekit-svg": "^4.1.3",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@tailwindcss/typography": "^0.5.10",
"@tsconfig/svelte": "^5.0.2",
@ -24,8 +25,7 @@
"tailwindcss": "^3.3.6",
"tslib": "^2.6.2",
"typescript": "^5.2.2",
"vite": "^5.0.0",
"vite-plugin-svelte-svg": "^2.3.0"
"vite": "^5.0.0"
},
"dependencies": {
"eva-icons": "^1.1.3",
@ -36,7 +36,16 @@
},
"prettier": {
"plugins": [
"prettier-plugin-svelte",
"prettier-plugin-tailwindcss"
],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte"
}
}
]
}
}

View file

@ -1,7 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
@apply bg-gray-50;
}

View file

@ -0,0 +1,5 @@
<div
class="rounded-lg border dark:border-gray-700 bg-white dark:bg-gray-800 m-2"
>
<slot />
</div>

View file

@ -0,0 +1,15 @@
<script lang="ts">
import ExternalLink from "eva-icons/outline/svg/external-link-outline.svg?component";
export let href: string;
</script>
<a
class="flex items-center leading-none text-gray-600 dark:text-gray-300 gap-1 pt-2"
{href}
target="_blank"
rel="noopener"
>
<ExternalLink fill="currentColor" class="h-4" />
<span class="underline">Fuente</span>
</a>

View file

@ -1,55 +1,47 @@
<script lang="ts">
import { inject } from "regexparam";
import ChevronRight from "eva-icons/outline/svg/chevron-right-outline.svg?component";
import { routes } from "../router";
import Portal from "../routes/Portal.svelte";
import { generateDumpName, routes } from "../router";
import NavItem from "./NavItem.svelte";
export let params:
| { dumpUrl: string }
| { dumpUrl: string; portal: string }
| { dumpUrl: string; portal: string; id: string };
function generateDumpName(dumpUrl: string) {
const clean = decodeURIComponent(dumpUrl).replace(/\/+$/, "");
return clean.slice(clean.lastIndexOf("/") + 1);
}
$: kind = "id" in params ? "dataset" : "portal" in params ? "portal" : "dump";
$: dumpName = generateDumpName(params.dumpUrl);
</script>
<nav class="flex justify-between m-2">
<nav class="m-2 flex justify-between">
<ol
class="flex items-center mb-3 text-sm text-neutral-500 [&_.active-breadcrumb]:text-neutral-600 [&_.active-breadcrumb]:font-medium sm:mb-0"
class="mb-3 flex items-center overflow-x-hidden text-sm text-neutral-500 dark:text-neutral-300 sm:mb-0 [&_.active-breadcrumb]:font-bold [&_.active-breadcrumb]:text-neutral-600 dark:[&_.active-breadcrumb]:text-neutral-200"
class:active-breadcrumb={kind === "dump"}
>
<li class="flex items-center h-full">
<a
href={inject(routes.Dump, params)}
class="inline-flex items-center px-2 py-1.5 space-x-1.5 rounded-md hover:text-neutral-900 hover:bg-neutral-100"
>
<span>{dumpName}</span>
</a>
</li>
<NavItem href={inject(routes.Dump, params)} active={kind === "dump"}
>{dumpName}</NavItem
>
{#if "portal" in params}
<ChevronRight class="w-5 h-5 text-gray-400" fill="currentColor" />
<li>
<a
href={inject(routes.Portal, params)}
class="inline-flex items-center px-2 py-1.5 space-x-1.5 font-normal rounded-md hover:bg-neutral-100 hover:text-neutral-900"
>
<span>{params.portal}</span>
</a>
</li>
<ChevronRight
class="h-5 w-5 shrink-0 text-neutral-400"
fill="currentColor"
/>
<NavItem href={inject(routes.Portal, params)} active={kind === "portal"}>
{params.portal}
</NavItem>
{/if}
{#if "id" in params}
<ChevronRight class="w-5 h-5 text-gray-400" fill="currentColor" />
<li>
<a
href={inject(routes.Dataset, params)}
class="inline-flex items-center px-2 py-1.5 space-x-1.5 font-normal rounded-md hover:bg-neutral-100 hover:text-neutral-900"
>
<span>{params.id}</span>
</a>
</li>
<ChevronRight
class="h-5 w-5 shrink-0 text-neutral-400"
fill="currentColor"
/>
<NavItem
href={inject(routes.Dataset, params)}
active={kind === "dataset"}
>
{params.id}
</NavItem>
{/if}
</ol>
</nav>

View file

@ -0,0 +1,14 @@
<script lang="ts">
export let active: boolean = false;
export let href: string;
</script>
<li class="min-w-0">
<a
{href}
class="block w-full space-x-1.5 overflow-hidden text-ellipsis whitespace-nowrap rounded-md px-2 py-1.5 hover:bg-neutral-100 hover:text-neutral-900 dark:hover:bg-gray-800 dark:hover:text-gray-100"
class:active-breadcrumb={active}
>
<slot />
</a>
</li>

View file

@ -8,13 +8,30 @@ export const routes = {
Dataset: "/dump/:dumpUrl/:portal/dataset/:id",
};
export function generateDumpName(dumpUrl: string) {
const clean = decodeURIComponent(dumpUrl).replace(/\/+$/, "");
return clean.slice(clean.lastIndexOf("/") + 1);
}
const title = "Archivo de portales de datos de Argentina";
const titles: { [key in ComponentType]: (params: Params) => string } = {
Home: () => title,
NotFound: () => title,
Dump: (params) => `${generateDumpName(params.dumpUrl)} - ${title}`,
Portal: (params) => `${params.portal} - ${title}`,
Dataset: (params) => `${params.id} - ${title}`,
};
export type ComponentType = "NotFound" | keyof typeof routes;
type Route = {
component: ComponentType;
params?: Params;
};
export const currentRoute = writable<Route>();
export const currentRoute = writable<Route>({ component: "Home" });
currentRoute.subscribe(
(route) => (document.title = titles[route.component](route.params!)),
);
export const router = navaid(undefined, () =>
currentRoute.set({ component: "NotFound" }),

View file

@ -1,11 +1,12 @@
<script lang="ts">
import ArrowBack from "eva-icons/outline/svg/arrow-back-outline.svg?component";
import ExternalLink from "eva-icons/outline/svg/external-link-outline.svg?component";
import { downloadFile, fetchData, fetchErrors } from "../fetch";
import NotFound from "./NotFound.svelte";
import { inject } from "regexparam";
import { routes } from "../router";
import Nav from "../nav/Nav.svelte";
import SourceLink from "../components/SourceLink.svelte";
import Container from "../components/Container.svelte";
export let params: { dumpUrl: string; portal: string; id: string };
$: url = decodeURIComponent(params.dumpUrl) + "/" + params.portal;
@ -18,7 +19,7 @@
<main class="mx-auto max-w-3xl">
<Nav {params} />
<div class="rounded-lg border bg-white m-2">
<Container>
{#await data}
<p class="p-6">Cargando dataset...</p>
{:then { data, errors }}
@ -26,18 +27,9 @@
{#if !dataset}
<NotFound />
{:else}
<header class="py-5 px-6 border-b border-b-gray-200">
<small>
<a
class="flex text-blue-500 leading-none gap-1 items-center"
href={inject(routes.Portal, {
dumpUrl: params.dumpUrl,
portal: params.portal,
})}
>
<ArrowBack fill="currentColor" class="h-[1.25em]" /> Viendo {data.title}
</a>
</small>
<header
class="py-5 px-6 border-b border-b-gray-200 dark:border-b-gray-700"
>
<h1 class="font-bold text-3xl">{dataset.title}</h1>
<p class="text-xl">{dataset.description}</p>
<!--
@ -55,7 +47,7 @@
</a>
{/if} -->
</header>
<ul class="divide-y divide-gray-100">
<ul class="divide-y divide-gray-100 dark:divide-gray-700">
{#each dataset.distribution as dist}
{@const error = errors.find(
(e) =>
@ -68,7 +60,7 @@
{dist.title}
{#if dist.format}
<span
class="border border-current text-blue-800 relative inline-flex items-center text-xs font-semibold px-2 py-1 rounded-full ml-1"
class="border border-current text-blue-800 dark:text-blue-400 relative inline-flex items-center text-xs font-semibold px-2 py-1 rounded-full ml-1"
>
<span>{dist.format}</span>
</span>
@ -99,15 +91,7 @@
>
{/if}
{#if dist.downloadURL}
<a
class="flex items-center leading-none text-gray-600 gap-1 pt-2"
href={dist.downloadURL}
target="_blank"
rel="noopener"
>
<ExternalLink fill="currentColor" class="h-4" />
Fuente
</a>
<SourceLink href={dist.downloadURL} />
{/if}
</div>
</li>
@ -115,5 +99,5 @@
</ul>
{/if}
{/await}
</div>
</Container>
</main>

View file

@ -1,8 +1,9 @@
<script lang="ts">
import { inject } from "regexparam";
import ExternalLink from "eva-icons/outline/svg/external-link-outline.svg?component";
import { fetchDumpMetadata } from "../fetch";
import { routes } from "../router";
import SourceLink from "../components/SourceLink.svelte";
import Container from "../components/Container.svelte";
export let params: { dumpUrl: string };
$: url = decodeURIComponent(params.dumpUrl);
@ -11,36 +12,25 @@
</script>
<main class="mx-auto max-w-3xl">
<div class="rounded-lg border bg-white m-2">
<Container>
{#await metadataPromise}
<p class="p-6">Cargando..</p>
{:then metadata}
<header class="py-5 px-6 border-b border-b-gray-200 leading-none">
<header
class="py-5 px-6 border-b border-b-gray-200 dark:border-b-gray-700 leading-none"
>
<small>
Viendo archivo en
<a
class="underline text-blue-500"
class="underline text-blue-500 dark:text-blue-300"
target="_blank"
rel="noopener"
href={url}>{url}</a
>
</small>
<!-- <h1 class="font-bold text-3xl">{data.title}</h1>
<p class="text-xl">{data.description}</p>
{#if data.homepage}
<a
class="flex items-center leading-none text-gray-600 gap-1 pt-2"
href={arreglarHomepageUrl(data.homepage)}
target="_blank"
rel="noopener"
>
<ExternalLink fill="currentColor" class="h-4" />
Fuente
</a>
{/if} -->
</header>
<ul class="divide-y divide-gray-100">
<ul class="divide-y divide-gray-100 dark:divide-gray-700">
{#each metadata.sites as site}
{@const portalLink = inject(routes.Portal, {
dumpUrl: params.dumpUrl,
@ -58,15 +48,7 @@
class="inline-flex items-center justify-center px-4 py-2 text-sm font-medium tracking-wide text-white transition-colors duration-200 bg-blue-600 rounded-md hover:bg-blue-700 focus:ring-2 focus:ring-offset-2 focus:ring-blue-700 focus:shadow-outline focus:outline-none"
>Ver portal</a
>
<a
class="flex items-center leading-none text-gray-600 gap-1 pt-2"
href={site.url}
target="_blank"
rel="noopener"
>
<ExternalLink fill="currentColor" class="h-4" />
Fuente
</a>
<SourceLink href={site.url} />
</div>
</div>
</li>
@ -75,5 +57,5 @@
{:catch error}
Hubo un error intenando cargar este archivo. <pre>{error}</pre>
{/await}
</div>
</Container>
</main>

View file

@ -1,44 +1,47 @@
<script lang="ts">
import { inject } from "regexparam";
import { routes } from "../router";
import Container from "../components/Container.svelte";
</script>
<main class="p-2">
<div class="mx-auto rounded-lg border bg-white py-5 px-6 prose">
<h1>Archivo de portales de datos abiertos</h1>
<p>
Esta herramienta permite ver datos en archivos de portales de datos
abiertos de <a
href="https://github.com/catdevnull/transicion-desordenada-diablo/"
rel="noopener">transicion-desordenada-diablo</a
>
(un mejor nombre sería genial), creada en el marco de
<a href="https://bit.ly/CartaDatosAbiertos">un pedido hecho</a> al gobierno
entrante el 10 de diciembre de 2023 por garantizar el mantenimiento de las
políticas de datos públicos en Argentina.
</p>
<main class="mx-auto prose dark:prose-invert">
<Container>
<div class="py-5 px-6">
<h1>Archivo de portales de datos abiertos</h1>
<p>
Esta herramienta permite ver datos en archivos de portales de datos
abiertos de <a
href="https://github.com/catdevnull/transicion-desordenada-diablo/"
rel="noopener">transicion-desordenada-diablo</a
>
(un mejor nombre sería genial), creada en el marco de
<a href="https://bit.ly/CartaDatosAbiertos">un pedido hecho</a> al gobierno
entrante el 10 de diciembre de 2023 por garantizar el mantenimiento de las
políticas de datos públicos en Argentina.
</p>
<div class="not-prose flex place-content-center">
<a
href={inject(routes.Dump, {
dumpUrl: encodeURIComponent(
"https://archivos.nulo.ar/dump-2023-12-08/",
),
})}
class="flex items-center justify-center px-4 py-2 text-xl font-medium text-white transition-colors duration-200 bg-blue-600 rounded-md hover:bg-blue-700 focus:ring-2 focus:ring-offset-2 focus:ring-blue-700 focus:shadow-outline focus:outline-none text-center"
>
Acceder al archivo creado el 8 de diciembre de 2023
</a>
<div class="not-prose flex place-content-center">
<a
href={inject(routes.Dump, {
dumpUrl: encodeURIComponent(
"https://archivos.nulo.ar/dump-2023-12-08/",
),
})}
class="flex items-center justify-center px-4 py-2 text-xl font-medium text-white transition-colors duration-200 bg-blue-600 rounded-md hover:bg-blue-700 focus:ring-2 focus:ring-offset-2 focus:ring-blue-700 focus:shadow-outline focus:outline-none text-center"
>
Acceder al archivo creado el 8 de diciembre de 2023
</a>
</div>
<p>
Los archivos y las herramientas fueron creados por
<a href="https://nulo.ar">Nulo</a> con ayuda de varias personas. El
código está disponible
<a
href="https://github.com/catdevnull/transicion-desordenada-diablo/"
rel="noopener">en GitHub</a
>.
</p>
</div>
<p>
Los archivos y las herramientas fueron creados por
<a href="https://nulo.ar">Nulo</a> con ayuda de varias personas. El código
está disponible
<a
href="https://github.com/catdevnull/transicion-desordenada-diablo/"
rel="noopener">en GitHub</a
>.
</p>
</div>
</Container>
</main>

View file

@ -1,11 +1,12 @@
<script lang="ts">
import { inject } from "regexparam";
import ArrowForward from "eva-icons/outline/svg/arrow-forward-outline.svg?component";
import ExternalLink from "eva-icons/outline/svg/external-link-outline.svg?component";
import { fetchData, fetchErrors } from "../fetch";
import { routes } from "../router";
import type { Dataset } from "common/schema";
import Nav from "../nav/Nav.svelte";
import SourceLink from "../components/SourceLink.svelte";
import Container from "../components/Container.svelte";
export let params: { dumpUrl: string; portal: string };
$: url = `${decodeURIComponent(params.dumpUrl)}/${params.portal}`;
@ -45,15 +46,17 @@
<main class="mx-auto max-w-3xl">
<Nav {params} />
<div class="rounded-lg border bg-white m-2">
<Container>
{#await data}
<p class="p-6">Cargando..</p>
{:then { data, errors }}
<header class="py-5 px-6 border-b border-b-gray-200 leading-none">
<header
class="py-5 px-6 border-b border-b-gray-200 dark:border-b-gray-700 leading-none"
>
<small>
Viendo portal archivado de
<a
class="underline text-blue-500"
class="underline text-blue-500 dark:text-blue-300"
target="_blank"
rel="noopener"
href={url}>{url}</a
@ -62,15 +65,7 @@
<h1 class="font-bold text-3xl">{data.title}</h1>
<p class="text-xl">{data.description}</p>
{#if data.homepage}
<a
class="flex items-center leading-none text-gray-600 gap-1 pt-2"
href={arreglarHomepageUrl(data.homepage)}
target="_blank"
rel="noopener"
>
<ExternalLink fill="currentColor" class="h-4" />
Fuente
</a>
<SourceLink href={arreglarHomepageUrl(data.homepage)} />
{/if}
</header>
@ -78,12 +73,12 @@
<input
type="text"
placeholder="Buscar..."
class="flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md border-neutral-300 ring-offset-background placeholder:text-neutral-500 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50"
class="flex w-full h-10 px-3 py-2 text-sm bg-white dark:bg-gray-800 border rounded-md border-neutral-300 dark:border-gray-700 ring-offset-background placeholder:text-neutral-500 dark:placeholder:text-gray-500 focus:border-neutral-300 dark:focus:border-gray-700 focus:outline-none focus:ring-2 focus:ring-neutral-400 dark:focus:ring-gray-600 disabled:cursor-not-allowed disabled:opacity-50"
bind:value={query}
/>
</div>
<ul class="divide-y divide-gray-100">
<ul class="divide-y divide-gray-100 dark:divide-gray-700">
{#each filterDatasets(data.dataset, query) as dataset}
{@const datasetLink = inject(routes.Dataset, {
dumpUrl: params.dumpUrl,
@ -92,7 +87,7 @@
})}
<li>
<a
class="flex px-6 py-5 hover:bg-gray-50 justify-between"
class="flex px-6 py-5 hover:bg-gray-50 dark:hover:bg-gray-700 justify-between"
href={datasetLink}
>
<div>
@ -102,7 +97,7 @@
<ArrowForward
fill="currentColor"
aria-hidden="true"
class="w-6 shrink-0 text-gray-600"
class="w-6 shrink-0 text-gray-600 dark:text-gray-400 "
/>
</a>
</li>
@ -111,5 +106,5 @@
{:catch error}
Hubo un error intenando cargar este portal archivado. <pre>{error}</pre>
{/await}
</div>
</Container>
</main>

View file

@ -1,17 +1,11 @@
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import svelteSVG from "vite-plugin-svelte-svg";
import svg from "@poppanator/sveltekit-svg";
// https://vitejs.dev/config/
export default defineConfig({
build: {
sourcemap: true,
},
plugins: [
svelte(),
svelteSVG({
svgoConfig: {}, // See https://github.com/svg/svgo#configuration
requireSuffix: true, // Set false to accept '.svg' without the '?component'
}),
],
plugins: [svelte(), svg()],
});

View file

@ -49,6 +49,9 @@ importers:
specifier: ^3.22.4
version: 3.22.4
devDependencies:
'@poppanator/sveltekit-svg':
specifier: ^4.1.3
version: 4.1.3(svelte@4.2.8)(svgo@3.0.5)(vite@5.0.7)
'@sveltejs/vite-plugin-svelte':
specifier: ^3.0.0
version: 3.0.1(svelte@4.2.8)(vite@5.0.7)
@ -94,9 +97,6 @@ importers:
vite:
specifier: ^5.0.0
version: 5.0.7
vite-plugin-svelte-svg:
specifier: ^2.3.0
version: 2.3.0(svelte@4.2.8)(vite@5.0.7)
packages:
@ -367,6 +367,18 @@ packages:
fastq: 1.15.0
dev: true
/@poppanator/sveltekit-svg@4.1.3(svelte@4.2.8)(svgo@3.0.5)(vite@5.0.7):
resolution: {integrity: sha512-cKdFxFPPzS470xy2XFQ2m/URa9On4fw7n5wvBqAwVO4sY8dmski+2N3GKFELt4tvzM3JPjAqz76Ex7U5IpKeIg==}
peerDependencies:
svelte: '>=4.x'
svgo: '>=3.x'
vite: '>=4.x'
dependencies:
svelte: 4.2.8
svgo: 3.0.5
vite: 5.0.7
dev: true
/@rollup/rollup-android-arm-eabi@4.7.0:
resolution: {integrity: sha512-rGku10pL1StFlFvXX5pEv88KdGW6DHUghsxyP/aRYb9eH+74jTGJ3U0S/rtlsQ4yYq1Hcc7AMkoJOb1xu29Fxw==}
cpu: [arm]
@ -1703,17 +1715,6 @@ packages:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
dev: true
/vite-plugin-svelte-svg@2.3.0(svelte@4.2.8)(vite@5.0.7):
resolution: {integrity: sha512-Bzo62u9Fj9PDldSvPrUgCzIFc9MmNbhxAUdBCofPyPyuVtoKaQxtsI01JXLk35x8/OQFHfxoTShEgPxtd4+c9Q==}
peerDependencies:
svelte: '>= 3.0.0 < 5.0.0'
vite: < 5.0.0
dependencies:
svelte: 4.2.8
svgo: 3.0.5
vite: 5.0.7
dev: true
/vite@5.0.7:
resolution: {integrity: sha512-B4T4rJCDPihrQo2B+h1MbeGL/k/GMAHzhQ8S0LjQ142s6/+l3hHTT095ORvsshj4QCkoWu3Xtmob5mazvakaOw==}
engines: {node: ^18.0.0 || >=20.0.0}