fix: resolve all TypeScript errors
- Fix Auth type: add currentWorkspace, workspaces, FlashData - Fix User.id type: number → string (UUID) - Fix trans() number params: wrap with String() - Fix debounce NodeJS.Timeout → ReturnType<typeof setTimeout> - Fix DatePicker name prop: required → optional - Fix DatePicker emit typing - Fix storeStep1 → storeRole import in onboarding/Role - Fix duplicate id in AcceptInvite interface - Fix formatDate → formatDateTime in ApiKeys - Add @unovis/vue and @unovis/ts type declarations - Install @unovis/vue @unovis/ts dev dependencies - Add type casts for Inertia/Reka UI component props
This commit is contained in:
parent
c020419db1
commit
07ddedceae
20 changed files with 1996 additions and 34 deletions
1931
package-lock.json
generated
1931
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -16,6 +16,8 @@
|
|||
"@laravel/vite-plugin-wayfinder": "^0.1.3",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@types/node": "^22.13.5",
|
||||
"@unovis/ts": "^1.6.4",
|
||||
"@unovis/vue": "^1.6.4",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
"@vue/eslint-config-typescript": "^14.3.0",
|
||||
"chokidar": "^5.0.0",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { createApp, h } from 'vue';
|
|||
import { initializeTheme } from './composables/useAppearance';
|
||||
import dayjs from './dayjs';
|
||||
import posthog from './posthog';
|
||||
import type { Auth } from './types';
|
||||
|
||||
configureEcho({
|
||||
broadcaster: 'reverb',
|
||||
|
|
@ -31,7 +32,7 @@ createInertiaApp({
|
|||
// Set dayjs locale based on user's language
|
||||
dayjs.locale(locale.toLowerCase());
|
||||
|
||||
const auth = props.initialPage.props.auth as { user?: { id: string; email: string; name: string }; currentWorkspace?: { id: string; name: string } } | undefined;
|
||||
const auth = props.initialPage.props.auth as Auth | undefined;
|
||||
|
||||
if (auth?.user) {
|
||||
posthog.identify(auth.user.id, {
|
||||
|
|
|
|||
|
|
@ -83,9 +83,9 @@ const remove = () => {
|
|||
const method = props.method as 'delete' | 'get' | 'post' | 'put' | 'patch';
|
||||
|
||||
if (method === 'delete' || method === 'get') {
|
||||
router[method](url.value, options);
|
||||
router[method](url.value, options as any);
|
||||
} else {
|
||||
router[method](url.value, {}, options);
|
||||
router[method](url.value, {}, options as any);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import dayjs from '@/dayjs';
|
|||
const props = defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '',
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
|
|
@ -47,7 +47,9 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string | null];
|
||||
}>();
|
||||
|
||||
// Parse input value into date
|
||||
const parseInput = (value: string) => {
|
||||
|
|
@ -180,7 +182,7 @@ const displayText = computed(() => {
|
|||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-auto p-0" :align="align">
|
||||
<Calendar v-model="internalDate" :placeholder="internalDate" layout="month-and-year" locale="en"
|
||||
<Calendar v-model="internalDate as any" :placeholder="(internalDate as any)" layout="month-and-year" locale="en"
|
||||
calendar-label="Date picker" initial-focus />
|
||||
<!-- Time Picker -->
|
||||
<div v-if="showTime" class="border-t p-3">
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ watchEffect(() => {
|
|||
<Combobox
|
||||
:model-value="selectedTimezone"
|
||||
@update:model-value="
|
||||
(v: Timezone) => {
|
||||
(v: any) => {
|
||||
selectedTimezone = v;
|
||||
emit('update:modelValue', v?.value || null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ import { computed, onMounted, watch } from 'vue';
|
|||
import { toast } from 'vue-sonner';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
|
||||
const style = computed(() => usePage().props.flash?.bannerStyle || 'success');
|
||||
const message = computed(() => usePage().props.flash?.banner || '');
|
||||
const page = usePage();
|
||||
const style = computed(() => (page.props.flash as Record<string, string>)?.bannerStyle || 'success');
|
||||
const message = computed(() => (page.props.flash as Record<string, string>)?.banner || '');
|
||||
|
||||
const showToast = (msg: string) => {
|
||||
switch (style.value) {
|
||||
|
|
|
|||
|
|
@ -403,7 +403,7 @@ const handleDropOnItem = (e: DragEvent, targetId: string) => {
|
|||
{{ validationMessage }}
|
||||
</span>
|
||||
</div>
|
||||
<Textarea :model-value="content" @update:model-value="emit('update:content', $event)"
|
||||
<Textarea :model-value="content" @update:model-value="emit('update:content', $event as string)"
|
||||
:placeholder="$t('posts.form.write_caption')" class="min-h-[120px] resize-none" :disabled="props.disabled" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ export default function debounce(
|
|||
callback: (...args: any[]) => void,
|
||||
wait = 1000,
|
||||
) {
|
||||
let timeoutId: NodeJS.Timeout | null = null;
|
||||
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
const debouncedFn = (...args: any[]) => {
|
||||
if (timeoutId) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import { type SharedData } from '@/types';
|
|||
|
||||
const props = defineProps<{
|
||||
invite: {
|
||||
id: string;
|
||||
id: string;
|
||||
email: string;
|
||||
role: {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ const getStatusVariant = (status: string): 'default' | 'secondary' | 'destructiv
|
|||
<IconSparkles class="h-4 w-4 text-primary" />
|
||||
<AlertTitle>{{ $t('billing.trial.title') }}</AlertTitle>
|
||||
<AlertDescription>
|
||||
{{ $t('billing.trial.description', { date: trialEndsAt }) }}
|
||||
{{ $t('billing.trial.description', { date: trialEndsAt ?? '' }) }}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ const featureKeys = [
|
|||
{{ $t('billing.subscribe.title') }}
|
||||
</h1>
|
||||
<p class="mt-2 text-sm text-muted-foreground">
|
||||
{{ trans('billing.subscribe.description', { days: displayDays }) }}
|
||||
{{ trans('billing.subscribe.description', { days: String(displayDays) }) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ const featureKeys = [
|
|||
:disabled="processing"
|
||||
@click="subscribe"
|
||||
>
|
||||
{{ trans('billing.subscribe.start_trial', { days: displayDays }) }}
|
||||
{{ trans('billing.subscribe.start_trial', { days: String(displayDays) }) }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ const getHashtagCount = (hashtags: string): number => {
|
|||
</div>
|
||||
</div>
|
||||
<CardDescription>
|
||||
{{ $t('hashtags.hashtags_count', { count: getHashtagCount(hashtag.hashtags) }) }}
|
||||
{{ $t('hashtags.hashtags_count', { count: String(getHashtagCount(hashtag.hashtags)) }) }}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Head, useForm } from '@inertiajs/vue3';
|
|||
import { IconBuilding, IconRocket, IconSparkles, IconBuildingStore, IconUser } from '@tabler/icons-vue';
|
||||
import { trans } from 'laravel-vue-i18n';
|
||||
|
||||
import { storeStep1 } from '@/actions/App/Http/Controllers/App/OnboardingController';
|
||||
import { storeRole } from '@/actions/App/Http/Controllers/App/OnboardingController';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import OnboardingLayout from '@/layouts/OnboardingLayout.vue';
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ const icons: Record<string, typeof IconRocket> = {
|
|||
};
|
||||
|
||||
const submit = () => {
|
||||
form.post(storeStep1.url());
|
||||
form.post(storeRole.url());
|
||||
};
|
||||
|
||||
const isSelected = (value: string) => form.persona === value;
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ const goToDate = (dateStr: string) => {
|
|||
});
|
||||
};
|
||||
|
||||
const switchView = (view: string) => {
|
||||
const switchView = (view: string | number) => {
|
||||
router.get(calendar.url({ query: { view } }), {}, {
|
||||
preserveState: true,
|
||||
});
|
||||
|
|
@ -270,7 +270,7 @@ const formatTime = (scheduledAt: string): string => {
|
|||
<Button variant="outline" size="icon" @click="navigate(1)">
|
||||
<IconChevronRight class="h-4 w-4" />
|
||||
</Button>
|
||||
<DatePicker v-if="isMobile" v-model="selectedDate" @update:model-value="goToDate" />
|
||||
<DatePicker v-if="isMobile" v-model="selectedDate" @update:model-value="(v: any) => goToDate(v)" />
|
||||
</template>
|
||||
|
||||
<template #header-center>
|
||||
|
|
@ -460,7 +460,7 @@ const formatTime = (scheduledAt: string): string => {
|
|||
</Link>
|
||||
<div v-if="getPostsForDay(day).length > 3"
|
||||
class="text-xs text-muted-foreground px-2 py-0.5">
|
||||
{{ $t('calendar.more', { count: getPostsForDay(day).length - 3 }) }}
|
||||
{{ $t('calendar.more', { count: String(getPostsForDay(day).length - 3) }) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -563,13 +563,13 @@ const contentValidation = computed(() => {
|
|||
} else if (hasUnsupportedVideos) {
|
||||
results[pp.id] = { valid: false, message: trans('posts.edit.validation.videos_not_supported'), charCount, maxLength: config.maxContentLength };
|
||||
} else if (hasTooManyImages) {
|
||||
results[pp.id] = { valid: false, message: trans('posts.edit.validation.max_images', { count: config.maxImages }), charCount, maxLength: config.maxContentLength };
|
||||
results[pp.id] = { valid: false, message: trans('posts.edit.validation.max_images', { count: String(config.maxImages) }), charCount, maxLength: config.maxContentLength };
|
||||
} else if (!config.supportsTextOnly && !hasMedia) {
|
||||
results[pp.id] = { valid: false, message: trans('posts.edit.validation.requires_media'), charCount, maxLength: config.maxContentLength };
|
||||
} else if (!hasContent && !hasMedia) {
|
||||
results[pp.id] = { valid: false, message: trans('posts.edit.no_content'), charCount, maxLength: config.maxContentLength };
|
||||
} else if (!withinLimit) {
|
||||
results[pp.id] = { valid: false, message: trans('posts.edit.validation.exceeded', { count: charCount - config.maxContentLength }), charCount, maxLength: config.maxContentLength };
|
||||
results[pp.id] = { valid: false, message: trans('posts.edit.validation.exceeded', { count: String(charCount - config.maxContentLength) }), charCount, maxLength: config.maxContentLength };
|
||||
} else {
|
||||
results[pp.id] = { valid: true, message: `${charCount}/${config.maxContentLength}`, charCount, maxLength: config.maxContentLength };
|
||||
}
|
||||
|
|
@ -594,7 +594,7 @@ const mediaValidation = computed(() => {
|
|||
}
|
||||
|
||||
if (imageCount > config.maxImages && config.maxImages > 0) {
|
||||
errors.push(trans('posts.edit.validation.supports_up_to_images', { platform: getPlatformLabel(pp.platform), count: config.maxImages }));
|
||||
errors.push(trans('posts.edit.validation.supports_up_to_images', { platform: getPlatformLabel(pp.platform), count: String(config.maxImages) }));
|
||||
}
|
||||
|
||||
if (!config.allowedMediaTypes.includes('video') && videoCount > 0) {
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ const confirmDeleteModal = ref<InstanceType<typeof ConfirmDeleteModal> | null>(n
|
|||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell class="text-muted-foreground">
|
||||
{{ token.expires_at ? date.formatDate(token.expires_at) : $t('settings.api_keys.table.never') }}
|
||||
{{ token.expires_at ? date.formatDateTime(token.expires_at) : $t('settings.api_keys.table.never') }}
|
||||
</TableCell>
|
||||
<TableCell class="text-muted-foreground">
|
||||
{{ token.last_used_at ? date.diffForHumans(token.last_used_at) : $t('settings.api_keys.table.never') }}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ const switchToWorkspace = (workspace: Workspace) => {
|
|||
<div class="min-w-0 flex-1">
|
||||
<p class="truncate font-medium">{{ workspace.name }}</p>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ trans('workspaces.connections', { count: workspace.social_accounts_count }) }} · {{ trans('workspaces.posts', { count: workspace.posts_count }) }}
|
||||
{{ trans('workspaces.connections', { count: String(workspace.social_accounts_count) }) }} · {{ trans('workspaces.posts', { count: String(workspace.posts_count) }) }}
|
||||
</p>
|
||||
</div>
|
||||
<Badge v-if="workspace.id === currentWorkspaceId" variant="secondary" class="shrink-0">
|
||||
|
|
|
|||
21
resources/js/types/globals.d.ts
vendored
21
resources/js/types/globals.d.ts
vendored
|
|
@ -1,5 +1,26 @@
|
|||
import { AppPageProps } from '@/types/index';
|
||||
|
||||
declare module '@unovis/vue' {
|
||||
export const VisXYContainer: any;
|
||||
export const VisLine: any;
|
||||
export const VisAxis: any;
|
||||
export const VisArea: any;
|
||||
export const VisStackedBar: any;
|
||||
export const VisBulletLegend: any;
|
||||
export const VisTooltip: any;
|
||||
export const VisCrosshair: any;
|
||||
}
|
||||
|
||||
declare module '@unovis/ts' {
|
||||
export class Line<T = any> {}
|
||||
export class Area<T = any> {}
|
||||
export class StackedBar<T = any> {}
|
||||
export class Axis<T = any> {}
|
||||
export class Crosshair<T = any> {}
|
||||
export class Tooltip<T = any> {}
|
||||
export class BulletLegend<T = any> {}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
dataLayer: Record<string, unknown>[];
|
||||
|
|
|
|||
21
resources/js/types/index.d.ts
vendored
21
resources/js/types/index.d.ts
vendored
|
|
@ -1,9 +1,27 @@
|
|||
import { InertiaLinkProps } from '@inertiajs/vue3';
|
||||
import type { Component } from 'vue';
|
||||
|
||||
export interface Workspace {
|
||||
id: string;
|
||||
name: string;
|
||||
logo_url: string | null;
|
||||
timezone: string;
|
||||
role?: 'owner' | 'admin' | 'member' | null;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface Auth {
|
||||
user: User;
|
||||
role: 'owner' | 'admin' | 'member' | null;
|
||||
currentWorkspace: Workspace | null;
|
||||
workspaces: Workspace[];
|
||||
}
|
||||
|
||||
export interface FlashData {
|
||||
banner?: string;
|
||||
bannerStyle?: 'success' | 'danger' | 'info' | 'warning';
|
||||
plainToken?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface BreadcrumbItem {
|
||||
|
|
@ -24,6 +42,7 @@ export interface NavItem {
|
|||
export interface SharedData {
|
||||
name: string;
|
||||
auth: Auth;
|
||||
flash: FlashData;
|
||||
sidebarOpen: boolean;
|
||||
selfHosted: boolean;
|
||||
[key: string]: unknown;
|
||||
|
|
@ -34,7 +53,7 @@ export type AppPageProps<
|
|||
> = T & SharedData;
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
has_photo: boolean;
|
||||
|
|
|
|||
Loading…
Reference in a new issue