Added more modules
Some checks failed
ci / ci (22, ubuntu-latest) (push) Has been cancelled

This commit is contained in:
2026-04-19 18:42:34 +01:00
parent 1e8cf0cb78
commit 08bb142c0a
9 changed files with 132 additions and 5 deletions

View File

@@ -11,7 +11,7 @@ const moduleDef = computed(() => modules[props.item.moduleName]);
// Create a render function component on the fly
const RenderedModule = () => {
if (moduleDef.value && typeof moduleDef.value.render === 'function') {
return moduleDef.value.render();
return moduleDef.value.render(props.item.params || {});
}
return null;
};

View File

@@ -3,6 +3,17 @@
moduleName: clock
parentId: null
order: -1
params:
label: "Local System Time"
- id: weather-widget
type: module
moduleName: weather
parentId: null
order: 0
params:
location: "Ninfield"
lat: 50.8765044
lng: 0.3943669
- id: media-folder
type: folder
parentId: null

View File

@@ -5,7 +5,9 @@ export const ClockModule: DashboardModule = {
name: 'clock',
width: 2,
height: 1,
render() {
render(params) {
const label = params.label || 'Local Time';
return h(defineComponent({
setup() {
const time = ref(new Date().toLocaleTimeString());
@@ -25,7 +27,7 @@ export const ClockModule: DashboardModule = {
'div',
{ class: 'w-full h-full flex flex-col items-center justify-center p-5 rounded-2xl bg-white/10 backdrop-blur-md border border-white/10 shadow-lg text-white group cursor-default transition-all' },
[
h('span', { class: 'text-sm text-white/50 mb-1 uppercase tracking-widest' }, 'Local Time'),
h('span', { class: 'text-sm text-white/50 mb-1 uppercase tracking-widest' }, label),
h('span', { class: 'text-3xl font-light tracking-tight text-shadow' }, time.value)
]
);

View File

@@ -1,7 +1,9 @@
import type { DashboardModule } from '../types/module';
import { ClockModule } from './clock';
import { WeatherModule } from './weather';
// Register all modules here
export const modules: Record<string, DashboardModule> = {
[ClockModule.name]: ClockModule
[ClockModule.name]: ClockModule,
[WeatherModule.name]: WeatherModule
};

95
app/modules/weather.ts Normal file
View File

@@ -0,0 +1,95 @@
import { h, ref, onMounted, defineComponent } from 'vue';
import { UIcon } from '#components';
import type { DashboardModule } from '../types/module';
// Map WMO weather codes to simple descriptions and Lucide icons
const getWeatherInfo = (code: number) => {
if (code === 0) return { label: 'Clear', icon: 'i-wi-day-sunny', color: 'text-yellow-400' };
if (code >= 1 && code < 4) return { label: 'Partly Cloudy', icon: 'i-wi-day-cloudy', color: 'text-gray-300' };
if (code >= 4 && code < 45) return { label: 'Cloudy', icon: 'i-wi-cloud', color: 'text-gray-300' };
if (code === 45 || code === 48) return { label: 'Fog', icon: 'i-wi-fog', color: 'text-gray-400' };
if (code >= 51 && code <= 55) return { label: 'Drizzle', icon: 'i-wi-rain-mix', color: 'text-blue-300' };
if (code >= 61 && code <= 65) return { label: 'Rain', icon: 'i-wi-rain', color: 'text-blue-400' };
if (code >= 71 && code <= 75) return { label: 'Snow', icon: 'i-wi-snow', color: 'text-white' };
if (code >= 80 && code <= 82) return { label: 'Showers', icon: 'i-wi-rain-wind', color: 'text-blue-500' };
if (code >= 95) return { label: 'Storm', icon: 'i-wi-storm-showers', color: 'text-purple-400' };
return { label: 'Unknown', icon: 'i-wi-na', color: 'text-gray-400' };
};
export const WeatherModule: DashboardModule = {
name: 'weather',
width: 2,
height: 1,
render(params) {
const locationName = params?.location || (params?.lat && params?.lng ? `${params.lat}, ${params.lng}` : 'London');
const inputLat = params?.lat;
const inputLng = params?.lng;
return h(defineComponent({
setup() {
const temp = ref<number | null>(null);
const condition = ref<{label: string, icon: string, color: string} | null>(null);
const loading = ref(true);
const error = ref(false);
onMounted(async () => {
try {
let latitude = inputLat;
let longitude = inputLng;
// 1. Get coordinates for location if lat/lng are not provided
if (latitude === undefined || longitude === undefined) {
const geoRes = await $fetch<any>(`https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(locationName)}&count=1`);
if (!geoRes.results || geoRes.results.length === 0) {
throw new Error('Location not found');
}
latitude = geoRes.results[0].latitude;
longitude = geoRes.results[0].longitude;
}
// 2. Fetch current weather
const weatherRes = await $fetch<any>(`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current_weather=true`);
temp.value = Math.round(weatherRes.current_weather.temperature);
condition.value = getWeatherInfo(weatherRes.current_weather.weathercode);
} catch (err) {
console.error(err);
error.value = true;
} finally {
loading.value = false;
}
});
return () => {
let content;
if (loading.value) {
content = h('div', { class: 'text-white/50 text-sm animate-pulse' }, 'Loading weather...');
} else if (error.value || !condition.value) {
content = h('div', { class: 'text-red-400/80 text-sm' }, 'Failed to load weather');
} else {
// The resolved weather info
content = h('div', { class: 'flex items-center gap-4 w-full justify-center' }, [
h(UIcon, { name: condition.value.icon, class: `${condition.value.color} w-20 h-20 drop-shadow-lg` }),
h('div', { class: 'flex flex-col' }, [
h('span', { class: 'text-3xl font-light tracking-tight text-shadow text-white' }, `${temp.value}°C`),
h('span', { class: 'text-sm text-white/50 font-medium tracking-wide' }, condition.value.label)
])
]);
}
return h(
'div',
{ class: 'w-full h-full flex flex-col items-center justify-center p-5 rounded-2xl bg-white/10 backdrop-blur-md border border-white/10 shadow-lg group transition-all relative overflow-hidden' },
[
h('div', { class: 'absolute top-3 left-4 text-[10px] text-white/40 uppercase tracking-widest font-bold' }, locationName),
content
]
);
};
}
}));
}
};

View File

@@ -4,5 +4,5 @@ export interface DashboardModule {
name: string;
width: number; // Number of grid columns to span
height: number; // Number of grid rows to span
render: () => VNode | any;
render: (params: Record<string, any>) => VNode | any;
}