feat: improve UI/UX
This commit is contained in:
@@ -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/*"]
|
||||||
|
|||||||
84
src/App.vue
84
src/App.vue
@@ -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 downloadCalendar = async () => {
|
const error = ref<string | null>(null);
|
||||||
const calendar = await extractData();
|
|
||||||
if (!calendar) {
|
|
||||||
// TODO: report error
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const downloadCalendar = async () => {
|
||||||
|
error.value = null;
|
||||||
|
|
||||||
|
await new Promise((resolve) =>
|
||||||
|
setTimeout(resolve, 1000 + Math.random() * 500),
|
||||||
|
);
|
||||||
|
|
||||||
|
let downloadUrl: string | null = null;
|
||||||
|
try {
|
||||||
|
// extract data and convert to ics
|
||||||
|
const calendar = await extractData();
|
||||||
const icsContent = generateIcsCalendar(calendar.data);
|
const icsContent = generateIcsCalendar(calendar.data);
|
||||||
|
|
||||||
const blob = new Blob([icsContent], { type: "text/calendar;charset=utf-8" });
|
// create blob from ics content then download
|
||||||
const url = URL.createObjectURL(blob);
|
const blob = new Blob([icsContent], {
|
||||||
try {
|
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>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<UButton
|
||||||
|
icon="i-lucide-github"
|
||||||
|
to="https://github.com/pihkaal/gestime-aphp-export"
|
||||||
|
target="_blank"
|
||||||
|
color="neutral"
|
||||||
|
variant="ghost"
|
||||||
|
size="md"
|
||||||
|
/>
|
||||||
|
<UColorModeButton size="md" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="space-y-2">
|
||||||
<UButton
|
<UButton
|
||||||
loading-auto
|
loading-auto
|
||||||
label="Download calendar"
|
label="Download Calendar"
|
||||||
|
icon="i-lucide-download"
|
||||||
|
size="lg"
|
||||||
|
block
|
||||||
|
color="secondary"
|
||||||
|
variant="subtle"
|
||||||
@click="downloadCalendar"
|
@click="downloadCalendar"
|
||||||
/>
|
/>
|
||||||
|
<UAlert
|
||||||
|
v-if="error"
|
||||||
|
color="error"
|
||||||
|
icon="i-lucide-circle-x"
|
||||||
|
:title="error"
|
||||||
|
/>
|
||||||
</div>
|
</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>
|
||||||
|
|||||||
@@ -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> {
|
}> {
|
||||||
try {
|
|
||||||
const [tab] = await chrome.tabs.query({
|
const [tab] = await chrome.tabs.query({
|
||||||
active: true,
|
active: true,
|
||||||
currentWindow: true,
|
currentWindow: true,
|
||||||
});
|
});
|
||||||
if (!tab?.id) {
|
if (!tab?.id) {
|
||||||
console.error("No active tab found");
|
throw new Error("URL incorrecte");
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlMatch = tab.url?.match(/\/(\d{2})-(\d{4})$/);
|
const urlMatch = tab.url?.match(/\/(\d{2})-(\d{4})$/);
|
||||||
if (!urlMatch) {
|
if (!urlMatch) {
|
||||||
console.error("Could not extract month and year from URL");
|
throw new Error("URL incorrecte");
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
const year = parseInt(urlMatch[2]!);
|
const year = parseInt(urlMatch[2]!);
|
||||||
const month = parseInt(urlMatch[1]!);
|
const month = parseInt(urlMatch[1]!);
|
||||||
|
|
||||||
|
try {
|
||||||
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`",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user