feat: improve UI/UX

This commit is contained in:
Pihkaal
2025-11-05 17:36:31 +01:00
parent d2c8591ddb
commit eb08d76bf2
3 changed files with 98 additions and 42 deletions

View File

@@ -2,9 +2,10 @@
"manifest_version": 3, "manifest_version": 3,
"name": "gestime.aphp.fr exporter", "name": "gestime.aphp.fr exporter",
"version": "1.0.0", "version": "1.0.0",
"description": "TBD", "description": "Export your Gestime work schedule to an ICS calendar file",
"action": { "action": {
"default_popup": "index.html" "default_popup": "index.html",
"default_popup_width": 450
}, },
"permissions": ["activeTab", "scripting"], "permissions": ["activeTab", "scripting"],
"host_permissions": ["http://gestime.aphp.fr/*"] "host_permissions": ["http://gestime.aphp.fr/*"]

View File

@@ -1,39 +1,95 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue";
import { extractData } from "@/utils/data"; import { extractData } from "@/utils/data";
import { generateIcsCalendar } from "ts-ics"; import { generateIcsCalendar } from "ts-ics";
const error = ref<string | null>(null);
const downloadCalendar = async () => { const downloadCalendar = async () => {
const calendar = await extractData(); error.value = null;
if (!calendar) {
// TODO: report error
return;
}
const icsContent = generateIcsCalendar(calendar.data); await new Promise((resolve) =>
setTimeout(resolve, 1000 + Math.random() * 500),
);
const blob = new Blob([icsContent], { type: "text/calendar;charset=utf-8" }); let downloadUrl: string | null = null;
const url = URL.createObjectURL(blob);
try { try {
// extract data and convert to ics
const calendar = await extractData();
const icsContent = generateIcsCalendar(calendar.data);
// create blob from ics content then download
const blob = new Blob([icsContent], {
type: "text/calendar;charset=utf-8",
});
downloadUrl = URL.createObjectURL(blob);
const link = document.createElement("a"); const link = document.createElement("a");
link.href = url; link.href = downloadUrl;
link.download = calendar.name; link.download = calendar.name;
link.click(); link.click();
} catch (err: unknown) {
error.value = err instanceof Error ? err.message : String(err);
} finally { } finally {
URL.revokeObjectURL(url); if (downloadUrl) URL.revokeObjectURL(downloadUrl);
} }
}; };
</script> </script>
<template> <template>
<UApp> <UApp>
<div class="app"> <UCard class="w-[450px] rounded-none">
<h1>Gestime APHP Export</h1> <template #header>
<div class="flex items-center justify-between">
<h1 class="text-xl font-bold text-gray-900 dark:text-white">
Gestime APHP Export
</h1>
<UButton <div class="flex items-center gap-2">
loading-auto <UButton
label="Download calendar" icon="i-lucide-github"
@click="downloadCalendar" to="https://github.com/pihkaal/gestime-aphp-export"
/> target="_blank"
</div> color="neutral"
variant="ghost"
size="md"
/>
<UColorModeButton size="md" />
</div>
</div>
</template>
<div class="space-y-4">
<div class="space-y-2">
<UButton
loading-auto
label="Download Calendar"
icon="i-lucide-download"
size="lg"
block
color="secondary"
variant="subtle"
@click="downloadCalendar"
/>
<UAlert
v-if="error"
color="error"
icon="i-lucide-circle-x"
:title="error"
/>
</div>
<UButton
label="Import in Google Calendar"
icon="i-lucide-external-link"
size="lg"
block
color="secondary"
variant="subtle"
to="https://calendar.google.com/calendar/u/0/r/settings/export"
target="_blank"
/>
</div>
</UCard>
</UApp> </UApp>
</template> </template>

View File

@@ -50,25 +50,23 @@ const makeIcsCalendar = (events: IcsEvent[]): IcsCalendar => ({
export async function extractData(): Promise<{ export async function extractData(): Promise<{
name: string; name: string;
data: IcsCalendar; data: IcsCalendar;
} | null> { }> {
const [tab] = await chrome.tabs.query({
active: true,
currentWindow: true,
});
if (!tab?.id) {
throw new Error("URL incorrecte");
}
const urlMatch = tab.url?.match(/\/(\d{2})-(\d{4})$/);
if (!urlMatch) {
throw new Error("URL incorrecte");
}
const year = parseInt(urlMatch[2]!);
const month = parseInt(urlMatch[1]!);
try { try {
const [tab] = await chrome.tabs.query({
active: true,
currentWindow: true,
});
if (!tab?.id) {
console.error("No active tab found");
return null;
}
const urlMatch = tab.url?.match(/\/(\d{2})-(\d{4})$/);
if (!urlMatch) {
console.error("Could not extract month and year from URL");
return null;
}
const year = parseInt(urlMatch[2]!);
const month = parseInt(urlMatch[1]!);
const [eventsQuery] = await chrome.scripting.executeScript({ const [eventsQuery] = await chrome.scripting.executeScript({
target: { tabId: tab.id }, target: { tabId: tab.id },
func: () => { func: () => {
@@ -105,7 +103,7 @@ export async function extractData(): Promise<{
return events; return events;
}, },
}); });
if (!eventsQuery?.result) return null; if (!eventsQuery?.result) throw undefined;
return { return {
name: `gestime-${urlMatch[1]}-${urlMatch[2]}.ics`, name: `gestime-${urlMatch[1]}-${urlMatch[2]}.ics`,
@@ -115,8 +113,9 @@ export async function extractData(): Promise<{
), ),
), ),
}; };
} catch (error) { } catch {
console.error("Error getting elements data:", error); throw new Error(
return null; "Impossible d'extraires les données, contactez `hello@pihkaal.me`",
);
} }
} }