diff --git a/.vscode/settings.json b/.vscode/settings.json index d4d338f6dc..7522198819 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,5 +29,7 @@ }, "[rust]": { "editor.defaultFormatter": "rust-lang.rust-analyzer" - } + }, + "css.lint.unknownAtRules": "ignore", + "scss.lint.unknownAtRules": "ignore" } diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index a4bc69aafc..06e392bf3d 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -970,13 +970,6 @@ provideAppUpdateDownloadProgress(appUpdateDownload) - - - + + +
diff --git a/apps/app-frontend/src/assets/stylesheets/global.scss b/apps/app-frontend/src/assets/stylesheets/global.scss index 346cb2abb8..a17170dcca 100644 --- a/apps/app-frontend/src/assets/stylesheets/global.scss +++ b/apps/app-frontend/src/assets/stylesheets/global.scss @@ -77,12 +77,8 @@ body { } a { - color: var(--color-link); + color: inherit; text-decoration: none; - - &:hover { - text-decoration: none; - } } .badge { @@ -174,4 +170,11 @@ img { } } +button, +input[type='button'] { + cursor: pointer; + border: none; + outline: 2px solid transparent; +} + @import '@modrinth/assets/omorphia.scss'; diff --git a/apps/frontend/src/assets/styles/global.scss b/apps/frontend/src/assets/styles/global.scss index b003bb72a9..3f9817684d 100644 --- a/apps/frontend/src/assets/styles/global.scss +++ b/apps/frontend/src/assets/styles/global.scss @@ -463,9 +463,9 @@ kbd { font-size: 0.85em !important; } -@import '~/assets/styles/layout.scss'; -@import '~/assets/styles/utils.scss'; -@import '~/assets/styles/components.scss'; +@import './layout.scss'; +@import './utils.scss'; +@import './components.scss'; // OMORPHIA FIXES .card { diff --git a/apps/frontend/src/pages/hosting/manage/[id].vue b/apps/frontend/src/pages/hosting/manage/[id].vue index bcf3667fcf..87e799c124 100644 --- a/apps/frontend/src/pages/hosting/manage/[id].vue +++ b/apps/frontend/src/pages/hosting/manage/[id].vue @@ -160,7 +160,7 @@ :show-loader-label="showLoaderLabel" :uptime-seconds="uptimeSeconds" :linked="true" - class="server-action-buttons-anim flex min-w-0 flex-col flex-wrap items-center gap-4 text-secondary *:hidden sm:flex-row sm:*:flex" + class="server-action-buttons-anim flex min-w-0 flex-col flex-wrap items-center gap-2 text-primary *:hidden sm:flex-row sm:*:flex" /> @@ -1135,7 +1135,8 @@ const handleInstallationResult = async (data: Archon.Websocket.v0.WSInstallation } const updateStats = (currentStats: Stats['current']) => { - isConnected.value = true + if (!isMounted.value) return + if (!isConnected.value) isConnected.value = true stats.value = { current: currentStats, past: { ...stats.value.current }, diff --git a/apps/frontend/src/pages/settings/billing/index.vue b/apps/frontend/src/pages/settings/billing/index.vue index 8cb481bc77..b90c57a72a 100644 --- a/apps/frontend/src/pages/settings/billing/index.vue +++ b/apps/frontend/src/pages/settings/billing/index.vue @@ -233,6 +233,7 @@ v-if="subscription.serverInfo" v-bind="subscription.serverInfo" :pending-change="getPendingChange(subscription)" + :cancellation-date="getCancellationDate(subscription)" />

@@ -919,6 +920,13 @@ const getPyroCharge = (subscription) => { ) } +const getCancellationDate = (subscription) => { + const charge = getPyroCharge(subscription) + if (!charge) return null + if (charge.status === 'cancelled') return charge.due + return null +} + const getProductSize = (product) => { if (!product || !product.metadata) return 'Unknown' const ramSize = product.metadata.ram diff --git a/packages/ui/.storybook/preview.ts b/packages/ui/.storybook/preview.ts index d775c85df0..9b340008eb 100644 --- a/packages/ui/.storybook/preview.ts +++ b/packages/ui/.storybook/preview.ts @@ -1,15 +1,22 @@ -import '@modrinth/assets/omorphia.scss' import 'floating-vue/dist/style.css' -import '../src/styles/tailwind.css' +import '../../assets/styles/defaults.scss' +// frontend css imports +// import '../../../apps/frontend/src/assets/styles/global.scss' +// import '../../../apps/frontend/src/assets/styles/tailwind.css' +// --- +// app-frontend css imports +import '../../../apps/app-frontend/src/assets/stylesheets/global.scss' import type { Labrinth } from '@modrinth/api-client' import { GenericModrinthClient } from '@modrinth/api-client' import { withThemeByClassName } from '@storybook/addon-themes' import type { Preview } from '@storybook/vue3-vite' import { setup } from '@storybook/vue3-vite' +import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query' import FloatingVue from 'floating-vue' -import { defineComponent, ref } from 'vue' +import { computed, defineComponent, h, ref } from 'vue' import { createI18n } from 'vue-i18n' +import { createMemoryHistory, createRouter } from 'vue-router' import NotificationPanel from '../src/components/nav/NotificationPanel.vue' import PopupNotificationPanel from '../src/components/nav/PopupNotificationPanel.vue' @@ -109,9 +116,68 @@ class StorybookPopupNotificationManager extends AbstractPopupNotificationManager } } +const StorybookLink = defineComponent({ + name: 'StorybookLink', + inheritAttrs: false, + props: { + to: { + type: [String, Object], + default: '', + }, + }, + setup(props, { attrs, slots }) { + const href = computed(() => { + if (typeof props.to === 'string') return props.to || '#' + if (props.to && typeof props.to === 'object' && 'path' in props.to) { + const path = props.to.path + return typeof path === 'string' ? path : '#' + } + return '#' + }) + + return () => + h( + 'a', + { + ...attrs, + href: href.value, + }, + slots.default?.(), + ) + }, +}) + +const StorybookClientOnly = defineComponent({ + name: 'StorybookClientOnly', + setup(_, { slots }) { + return () => slots.default?.() + }, +}) + setup((app) => { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + }, + mutations: { + retry: false, + }, + }, + }) + app.use(VueQueryPlugin, { queryClient }) app.use(i18n) + const router = createRouter({ + history: createMemoryHistory(), + routes: [{ path: '/:pathMatch(.*)*', component: { render: () => null } }], + }) + app.use(router) + + app.component('NuxtLink', StorybookLink) + app.component('RouterLink', StorybookLink) + app.component('ClientOnly', StorybookClientOnly) + // Provide the custom I18nContext for components using injectI18n() const i18nContext: I18nContext = { locale: i18n.global.locale, diff --git a/packages/ui/src/components/base/CopyCode.vue b/packages/ui/src/components/base/CopyCode.vue index f046b5f12c..7e3edb33f6 100644 --- a/packages/ui/src/components/base/CopyCode.vue +++ b/packages/ui/src/components/base/CopyCode.vue @@ -1,5 +1,9 @@