diff --git a/next-ui/src/components.d.ts b/next-ui/src/components.d.ts
index 8d92a2673..0ac268538 100644
--- a/next-ui/src/components.d.ts
+++ b/next-ui/src/components.d.ts
@@ -30,6 +30,7 @@ declare module 'vue' {
LayoutAppDrawerMenuLogout: typeof import('./fragments/layout/app/drawer/menu/Logout.vue')['default']
LayoutAppDrawerMenuMedia: typeof import('./fragments/layout/app/drawer/menu/Media.vue')['default']
LayoutAppDrawerMenuServer: typeof import('./fragments/layout/app/drawer/menu/Server.vue')['default']
+ ReleaseCard: typeof import('./components/release/Card.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
UserDeletionWarning: typeof import('./components/user/DeletionWarning.vue')['default']
diff --git a/next-ui/src/components/release/Card.stories.ts b/next-ui/src/components/release/Card.stories.ts
new file mode 100644
index 000000000..cc1e1c05e
--- /dev/null
+++ b/next-ui/src/components/release/Card.stories.ts
@@ -0,0 +1,57 @@
+import type { Meta, StoryObj } from '@storybook/vue3-vite'
+
+import Card from './Card.vue'
+
+const meta = {
+ component: Card,
+ render: (args: object) => ({
+ components: { Card },
+ setup() {
+ return { args }
+ },
+ template: '',
+ }),
+ parameters: {
+ // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
+ },
+ args: {},
+} satisfies Meta
+
+export default meta
+type Story = StoryObj
+
+export const Default: Story = {
+ args: {
+ release: {
+ version: '1.21.1',
+ releaseDate: new Date('2025-03-06T07:31:00Z'),
+ url: 'https://github.com/gotson/komga/releases/tag/1.21.1',
+ latest: false,
+ preRelease: false,
+ description:
+ "## Changelog\n\n## 🐛 Fixes\n**api**\n- book import would return incorrect matched series ([10e0bde](https://github.com/gotson/komga/commits/10e0bde))\n\n\n## Contributors\nWe'd like to thank the following people for their contributions:\nGauthier Roebroeck",
+ },
+ },
+}
+
+export const Latest: Story = {
+ args: {
+ ...Default.args,
+ latest: true,
+ },
+}
+
+export const Current: Story = {
+ args: {
+ ...Default.args,
+ current: true,
+ },
+}
+
+export const LatestAndCurrent: Story = {
+ args: {
+ ...Default.args,
+ latest: true,
+ current: true,
+ },
+}
diff --git a/next-ui/src/components/release/Card.vue b/next-ui/src/components/release/Card.vue
new file mode 100644
index 000000000..545bdafba
--- /dev/null
+++ b/next-ui/src/components/release/Card.vue
@@ -0,0 +1,86 @@
+
+
+
+
+
{{ release.version }}
+
+ {{
+ $formatMessage({
+ description:
+ 'Updates view: badge showing next to the currently installed release number',
+ defaultMessage: 'Currently installed',
+ id: '3jrAF6',
+ })
+ }}
+
+
+ {{
+ $formatMessage({
+ description: 'Updates view: badge showing next to the latest release number',
+ defaultMessage: 'Latest',
+ id: '2Bh8F2',
+ })
+ }}
+
+
+
+
+
+ {{ $formatDate(release.releaseDate, { dateStyle: 'long' }) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/next-ui/src/pages/server/announcements.vue b/next-ui/src/pages/server/announcements.vue
index 49a75e554..620cfd681 100644
--- a/next-ui/src/pages/server/announcements.vue
+++ b/next-ui/src/pages/server/announcements.vue
@@ -22,6 +22,7 @@
@mark-read="markRead"
/>
+
-
-
-
-
{{ release.version }}
-
- {{
- $formatMessage({
- description:
- 'Updates view: badge showing next to the currently installed release number',
- defaultMessage: 'Currently installed',
- id: '3jrAF6',
- })
- }}
-
-
- {{
- $formatMessage({
- description: 'Updates view: badge showing next to the latest release number',
- defaultMessage: 'Latest',
- id: '2Bh8F2',
- })
- }}
-
-
-
-
-
- {{ $formatDate(release.releaseDate, { dateStyle: 'long' }) }}
-
-
-
-
-
-
-
-
+
-
-
meta:
requiresRole: ADMIN