Commit 3d05e503 authored by shaw's avatar shaw
Browse files

fix: frontend build error

parent 642842c2
This diff is collapsed.
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vue-tsc && vite build", "build": "vue-tsc -b && vite build",
"preview": "vite preview", "preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix" "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
"typecheck": "vue-tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@vueuse/core": "^10.7.0", "@vueuse/core": "^10.7.0",
...@@ -21,12 +22,13 @@ ...@@ -21,12 +22,13 @@
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.10.5", "@types/node": "^20.10.5",
"@vitejs/plugin-vue": "^4.5.2", "@vitejs/plugin-vue": "^5.2.3",
"autoprefixer": "^10.4.16", "autoprefixer": "^10.4.16",
"postcss": "^8.4.32", "postcss": "^8.4.32",
"tailwindcss": "^3.4.0", "tailwindcss": "^3.4.0",
"typescript": "^5.3.3", "typescript": "~5.6.0",
"vite": "^5.0.10", "vite": "^5.0.10",
"vue-tsc": "^1.8.25" "vite-plugin-checker": "^0.9.1",
"vue-tsc": "^2.2.0"
} }
} }
...@@ -23,7 +23,7 @@ export async function list( ...@@ -23,7 +23,7 @@ export async function list(
pageSize: number = 20, pageSize: number = 20,
filters?: { filters?: {
type?: RedeemCodeType; type?: RedeemCodeType;
status?: 'active' | 'used' | 'expired'; status?: 'active' | 'used' | 'expired' | 'unused';
search?: string; search?: string;
} }
): Promise<PaginatedResponse<RedeemCode>> { ): Promise<PaginatedResponse<RedeemCode>> {
......
...@@ -506,7 +506,7 @@ ...@@ -506,7 +506,7 @@
{{ t('common.back') }} {{ t('common.back') }}
</button> </button>
<button <button
v-if="oauthFlowRef?.inputMethod?.value === 'manual'" v-if="isManualInputMethod"
type="button" type="button"
:disabled="!canExchangeCode" :disabled="!canExchangeCode"
class="btn btn-primary" class="btn btn-primary"
...@@ -533,14 +533,22 @@ import { ref, reactive, computed, watch } from 'vue' ...@@ -533,14 +533,22 @@ import { ref, reactive, computed, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app' import { useAppStore } from '@/stores/app'
import { adminAPI } from '@/api/admin' import { adminAPI } from '@/api/admin'
import { useAccountOAuth, type AddMethod } from '@/composables/useAccountOAuth' import { useAccountOAuth, type AddMethod, type AuthInputMethod } from '@/composables/useAccountOAuth'
import type { Proxy, Group, AccountPlatform, AccountType } from '@/types' import type { Proxy, Group, AccountPlatform, AccountType } from '@/types'
import Modal from '@/components/common/Modal.vue' import Modal from '@/components/common/Modal.vue'
import Select from '@/components/common/Select.vue'
import ProxySelector from '@/components/common/ProxySelector.vue' import ProxySelector from '@/components/common/ProxySelector.vue'
import GroupSelector from '@/components/common/GroupSelector.vue' import GroupSelector from '@/components/common/GroupSelector.vue'
import OAuthAuthorizationFlow from './OAuthAuthorizationFlow.vue' import OAuthAuthorizationFlow from './OAuthAuthorizationFlow.vue'
// Type for exposed OAuthAuthorizationFlow component
// Note: defineExpose automatically unwraps refs, so we use the unwrapped types
interface OAuthFlowExposed {
authCode: string
sessionKey: string
inputMethod: AuthInputMethod
reset: () => void
}
const { t } = useI18n() const { t } = useI18n()
interface Props { interface Props {
...@@ -561,7 +569,7 @@ const appStore = useAppStore() ...@@ -561,7 +569,7 @@ const appStore = useAppStore()
const oauth = useAccountOAuth() const oauth = useAccountOAuth()
// Refs // Refs
const oauthFlowRef = ref<InstanceType<typeof OAuthAuthorizationFlow> | null>(null) const oauthFlowRef = ref<OAuthFlowExposed | null>(null)
// Model mapping type // Model mapping type
interface ModelMapping { interface ModelMapping {
...@@ -630,8 +638,12 @@ const form = reactive({ ...@@ -630,8 +638,12 @@ const form = reactive({
// Helper to check if current type needs OAuth flow // Helper to check if current type needs OAuth flow
const isOAuthFlow = computed(() => accountCategory.value === 'oauth-based') const isOAuthFlow = computed(() => accountCategory.value === 'oauth-based')
const isManualInputMethod = computed(() => {
return oauthFlowRef.value?.inputMethod === 'manual'
})
const canExchangeCode = computed(() => { const canExchangeCode = computed(() => {
const authCode = oauthFlowRef.value?.authCode?.value || '' const authCode = oauthFlowRef.value?.authCode || ''
return authCode.trim() && oauth.sessionId.value && !oauth.loading.value return authCode.trim() && oauth.sessionId.value && !oauth.loading.value
}) })
...@@ -815,7 +827,7 @@ const handleGenerateUrl = async () => { ...@@ -815,7 +827,7 @@ const handleGenerateUrl = async () => {
} }
const handleExchangeCode = async () => { const handleExchangeCode = async () => {
const authCode = oauthFlowRef.value?.authCode?.value || '' const authCode = oauthFlowRef.value?.authCode || ''
if (!authCode.trim() || !oauth.sessionId.value) return if (!authCode.trim() || !oauth.sessionId.value) return
oauth.loading.value = true oauth.loading.value = true
......
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
{{ t('common.cancel') }} {{ t('common.cancel') }}
</button> </button>
<button <button
v-if="oauthFlowRef?.inputMethod?.value === 'manual'" v-if="isManualInputMethod"
type="button" type="button"
:disabled="!canExchangeCode" :disabled="!canExchangeCode"
class="btn btn-primary" class="btn btn-primary"
...@@ -98,11 +98,20 @@ import { ref, computed, watch } from 'vue' ...@@ -98,11 +98,20 @@ import { ref, computed, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app' import { useAppStore } from '@/stores/app'
import { adminAPI } from '@/api/admin' import { adminAPI } from '@/api/admin'
import { useAccountOAuth, type AddMethod } from '@/composables/useAccountOAuth' import { useAccountOAuth, type AddMethod, type AuthInputMethod } from '@/composables/useAccountOAuth'
import type { Account } from '@/types' import type { Account } from '@/types'
import Modal from '@/components/common/Modal.vue' import Modal from '@/components/common/Modal.vue'
import OAuthAuthorizationFlow from './OAuthAuthorizationFlow.vue' import OAuthAuthorizationFlow from './OAuthAuthorizationFlow.vue'
// Type for exposed OAuthAuthorizationFlow component
// Note: defineExpose automatically unwraps refs, so we use the unwrapped types
interface OAuthFlowExposed {
authCode: string
sessionKey: string
inputMethod: AuthInputMethod
reset: () => void
}
interface Props { interface Props {
show: boolean show: boolean
account: Account | null account: Account | null
...@@ -121,14 +130,18 @@ const { t } = useI18n() ...@@ -121,14 +130,18 @@ const { t } = useI18n()
const oauth = useAccountOAuth() const oauth = useAccountOAuth()
// Refs // Refs
const oauthFlowRef = ref<InstanceType<typeof OAuthAuthorizationFlow> | null>(null) const oauthFlowRef = ref<OAuthFlowExposed | null>(null)
// State // State
const addMethod = ref<AddMethod>('oauth') const addMethod = ref<AddMethod>('oauth')
// Computed // Computed
const isManualInputMethod = computed(() => {
return oauthFlowRef.value?.inputMethod === 'manual'
})
const canExchangeCode = computed(() => { const canExchangeCode = computed(() => {
const authCode = oauthFlowRef.value?.authCode?.value || '' const authCode = oauthFlowRef.value?.authCode || ''
return authCode.trim() && oauth.sessionId.value && !oauth.loading.value return authCode.trim() && oauth.sessionId.value && !oauth.loading.value
}) })
...@@ -163,7 +176,7 @@ const handleGenerateUrl = async () => { ...@@ -163,7 +176,7 @@ const handleGenerateUrl = async () => {
const handleExchangeCode = async () => { const handleExchangeCode = async () => {
if (!props.account) return if (!props.account) return
const authCode = oauthFlowRef.value?.authCode?.value || '' const authCode = oauthFlowRef.value?.authCode || ''
if (!authCode.trim() || !oauth.sessionId.value) return if (!authCode.trim() || !oauth.sessionId.value) return
oauth.loading.value = true oauth.loading.value = true
......
...@@ -47,7 +47,7 @@ interface Emits { ...@@ -47,7 +47,7 @@ interface Emits {
(e: 'cancel'): void (e: 'cancel'): void
} }
const props = withDefaults(defineProps<Props>(), { withDefaults(defineProps<Props>(), {
confirmText: 'Confirm', confirmText: 'Confirm',
cancelText: 'Cancel', cancelText: 'Cancel',
danger: false danger: false
......
...@@ -86,16 +86,10 @@ ...@@ -86,16 +86,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import type { Column } from './types'
const { t } = useI18n() const { t } = useI18n()
export interface Column {
key: string
label: string
sortable?: boolean
formatter?: (value: any, row: any) => string
}
interface Props { interface Props {
columns: Column[] columns: Column[]
data: any[] data: any[]
......
...@@ -69,7 +69,6 @@ ...@@ -69,7 +69,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Component } from 'vue' import type { Component } from 'vue'
import { RouterLink } from 'vue-router'
interface Props { interface Props {
icon?: Component | string icon?: Component | string
...@@ -81,7 +80,7 @@ interface Props { ...@@ -81,7 +80,7 @@ interface Props {
message?: string message?: string
} }
const props = withDefaults(defineProps<Props>(), { withDefaults(defineProps<Props>(), {
title: 'No data found', title: 'No data found',
description: '', description: '',
actionIcon: true actionIcon: true
......
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
<div class="select-options"> <div class="select-options">
<div <div
v-for="option in filteredOptions" v-for="option in filteredOptions"
:key="getOptionValue(option)" :key="getOptionValue(option) ?? undefined"
@click="selectOption(option)" @click="selectOption(option)"
:class="[ :class="[
'select-option', 'select-option',
...@@ -136,9 +136,9 @@ const searchQuery = ref('') ...@@ -136,9 +136,9 @@ const searchQuery = ref('')
const containerRef = ref<HTMLElement | null>(null) const containerRef = ref<HTMLElement | null>(null)
const searchInputRef = ref<HTMLInputElement | null>(null) const searchInputRef = ref<HTMLInputElement | null>(null)
const getOptionValue = (option: SelectOption | Record<string, unknown>): string | number | null => { const getOptionValue = (option: SelectOption | Record<string, unknown>): string | number | null | undefined => {
if (typeof option === 'object' && option !== null) { if (typeof option === 'object' && option !== null) {
return option[props.valueKey] as string | number | null return option[props.valueKey] as string | number | null | undefined
} }
return option as string | number | null return option as string | number | null
} }
...@@ -187,7 +187,7 @@ const toggle = () => { ...@@ -187,7 +187,7 @@ const toggle = () => {
} }
const selectOption = (option: SelectOption | Record<string, unknown>) => { const selectOption = (option: SelectOption | Record<string, unknown>) => {
const value = getOptionValue(option) const value = getOptionValue(option) ?? null
emit('update:modelValue', value) emit('update:modelValue', value)
emit('change', value, option as SelectOption) emit('change', value, option as SelectOption)
isOpen.value = false isOpen.value = false
......
...@@ -121,7 +121,7 @@ ...@@ -121,7 +121,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'; import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import subscriptionsAPI from '@/api/subscriptions'; import subscriptionsAPI from '@/api/subscriptions';
import type { UserSubscription } from '@/types'; import type { UserSubscription } from '@/types';
......
...@@ -10,4 +10,4 @@ export { default as EmptyState } from './EmptyState.vue' ...@@ -10,4 +10,4 @@ export { default as EmptyState } from './EmptyState.vue'
export { default as LocaleSwitcher } from './LocaleSwitcher.vue' export { default as LocaleSwitcher } from './LocaleSwitcher.vue'
// Export types // Export types
export type { Column } from './DataTable.vue' export type { Column } from './types'
/**
* Common component types
*/
export interface Column {
key: string
label: string
sortable?: boolean
formatter?: (value: any, row: any) => string
}
...@@ -268,7 +268,7 @@ const routes: RouteRecordRaw[] = [ ...@@ -268,7 +268,7 @@ const routes: RouteRecordRaw[] = [
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
routes, routes,
scrollBehavior(to, from, savedPosition) { scrollBehavior(_to, _from, savedPosition) {
// Scroll to saved position when using browser back/forward // Scroll to saved position when using browser back/forward
if (savedPosition) { if (savedPosition) {
return savedPosition; return savedPosition;
...@@ -283,7 +283,7 @@ const router = createRouter({ ...@@ -283,7 +283,7 @@ const router = createRouter({
*/ */
let authInitialized = false; let authInitialized = false;
router.beforeEach((to, from, next) => { router.beforeEach((to, _from, next) => {
const authStore = useAuthStore(); const authStore = useAuthStore();
// Restore auth state from localStorage on first navigation (page refresh) // Restore auth state from localStorage on first navigation (page refresh)
......
...@@ -152,6 +152,12 @@ export interface UserStats { ...@@ -152,6 +152,12 @@ export interface UserStats {
// ==================== API Response Types ==================== // ==================== API Response Types ====================
export interface ApiResponse<T = unknown> {
code: number;
message: string;
data: T;
}
export interface ApiError { export interface ApiError {
detail: string; detail: string;
code?: string; code?: string;
...@@ -357,6 +363,7 @@ export interface CreateAccountRequest { ...@@ -357,6 +363,7 @@ export interface CreateAccountRequest {
export interface UpdateAccountRequest { export interface UpdateAccountRequest {
name?: string; name?: string;
type?: AccountType;
credentials?: Record<string, unknown>; credentials?: Record<string, unknown>;
extra?: Record<string, string>; extra?: Record<string, string>;
proxy_id?: number | null; proxy_id?: number | null;
......
...@@ -265,7 +265,7 @@ import { useI18n } from 'vue-i18n' ...@@ -265,7 +265,7 @@ import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app' import { useAppStore } from '@/stores/app'
import { adminAPI } from '@/api/admin' import { adminAPI } from '@/api/admin'
import type { Account, Proxy, Group } from '@/types' import type { Account, Proxy, Group } from '@/types'
import type { Column } from '@/components/common/DataTable.vue' import type { Column } from '@/components/common/types'
import AppLayout from '@/components/layout/AppLayout.vue' import AppLayout from '@/components/layout/AppLayout.vue'
import DataTable from '@/components/common/DataTable.vue' import DataTable from '@/components/common/DataTable.vue'
import Pagination from '@/components/common/Pagination.vue' import Pagination from '@/components/common/Pagination.vue'
......
...@@ -398,7 +398,7 @@ const lineOptions = computed(() => ({ ...@@ -398,7 +398,7 @@ const lineOptions = computed(() => ({
font: { font: {
size: 10, size: 10,
}, },
callback: (value: number) => formatTokens(value), callback: (value: string | number) => formatTokens(Number(value)),
}, },
}, },
}, },
......
...@@ -450,7 +450,7 @@ import { useI18n } from 'vue-i18n' ...@@ -450,7 +450,7 @@ import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app' import { useAppStore } from '@/stores/app'
import { adminAPI } from '@/api/admin' import { adminAPI } from '@/api/admin'
import type { Group, GroupPlatform, SubscriptionType } from '@/types' import type { Group, GroupPlatform, SubscriptionType } from '@/types'
import type { Column } from '@/components/common/DataTable.vue' import type { Column } from '@/components/common/types'
import AppLayout from '@/components/layout/AppLayout.vue' import AppLayout from '@/components/layout/AppLayout.vue'
import DataTable from '@/components/common/DataTable.vue' import DataTable from '@/components/common/DataTable.vue'
import Pagination from '@/components/common/Pagination.vue' import Pagination from '@/components/common/Pagination.vue'
...@@ -570,7 +570,7 @@ const loadGroups = async () => { ...@@ -570,7 +570,7 @@ const loadGroups = async () => {
pagination.page, pagination.page,
pagination.page_size, pagination.page_size,
{ {
platform: filters.platform || undefined, platform: (filters.platform as GroupPlatform) || undefined,
status: filters.status as any, status: filters.status as any,
is_exclusive: filters.is_exclusive ? filters.is_exclusive === 'true' : undefined is_exclusive: filters.is_exclusive ? filters.is_exclusive === 'true' : undefined
} }
......
...@@ -465,7 +465,7 @@ import { useI18n } from 'vue-i18n' ...@@ -465,7 +465,7 @@ import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app' import { useAppStore } from '@/stores/app'
import { adminAPI } from '@/api/admin' import { adminAPI } from '@/api/admin'
import type { Proxy, ProxyProtocol } from '@/types' import type { Proxy, ProxyProtocol } from '@/types'
import type { Column } from '@/components/common/DataTable.vue' import type { Column } from '@/components/common/types'
import AppLayout from '@/components/layout/AppLayout.vue' import AppLayout from '@/components/layout/AppLayout.vue'
import DataTable from '@/components/common/DataTable.vue' import DataTable from '@/components/common/DataTable.vue'
import Pagination from '@/components/common/Pagination.vue' import Pagination from '@/components/common/Pagination.vue'
......
...@@ -349,7 +349,7 @@ import { useI18n } from 'vue-i18n' ...@@ -349,7 +349,7 @@ import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app' import { useAppStore } from '@/stores/app'
import { adminAPI } from '@/api/admin' import { adminAPI } from '@/api/admin'
import type { RedeemCode, RedeemCodeType, Group } from '@/types' import type { RedeemCode, RedeemCodeType, Group } from '@/types'
import type { Column } from '@/components/common/DataTable.vue' import type { Column } from '@/components/common/types'
import AppLayout from '@/components/layout/AppLayout.vue' import AppLayout from '@/components/layout/AppLayout.vue'
import DataTable from '@/components/common/DataTable.vue' import DataTable from '@/components/common/DataTable.vue'
import Pagination from '@/components/common/Pagination.vue' import Pagination from '@/components/common/Pagination.vue'
......
...@@ -303,7 +303,7 @@ import { useI18n } from 'vue-i18n' ...@@ -303,7 +303,7 @@ import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app' import { useAppStore } from '@/stores/app'
import { adminAPI } from '@/api/admin' import { adminAPI } from '@/api/admin'
import type { UserSubscription, Group, User } from '@/types' import type { UserSubscription, Group, User } from '@/types'
import type { Column } from '@/components/common/DataTable.vue' import type { Column } from '@/components/common/types'
import AppLayout from '@/components/layout/AppLayout.vue' import AppLayout from '@/components/layout/AppLayout.vue'
import DataTable from '@/components/common/DataTable.vue' import DataTable from '@/components/common/DataTable.vue'
import Pagination from '@/components/common/Pagination.vue' import Pagination from '@/components/common/Pagination.vue'
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment