diff --git a/apps/login/locales/de.json b/apps/login/locales/de.json
index 171f6c705d..40529341f9 100644
--- a/apps/login/locales/de.json
+++ b/apps/login/locales/de.json
@@ -10,7 +10,8 @@
},
"logout": {
"title": "Logout",
- "description": "Wählen Sie den Account aus, das Sie entfernen möchten"
+ "description": "Wählen Sie den Account aus, das Sie entfernen möchten",
+ "noResults": "Keine Konten gefunden"
},
"loginname": {
"title": "Willkommen zurück!",
diff --git a/apps/login/locales/en.json b/apps/login/locales/en.json
index dbf3ac6427..2b05693e59 100644
--- a/apps/login/locales/en.json
+++ b/apps/login/locales/en.json
@@ -10,7 +10,8 @@
},
"logout": {
"title": "Logout",
- "description": "Select the account you want to clear"
+ "description": "Click the accounts you want to clear",
+ "noResults": "No accounts found"
},
"loginname": {
"title": "Welcome back!",
diff --git a/apps/login/locales/es.json b/apps/login/locales/es.json
index cf54ea351e..c89cf2b4b2 100644
--- a/apps/login/locales/es.json
+++ b/apps/login/locales/es.json
@@ -10,7 +10,8 @@
},
"logout": {
"title": "Cerrar sesión",
- "description": "Selecciona la cuenta que deseas eliminar"
+ "description": "Selecciona la cuenta que deseas eliminar",
+ "noResults": "No se encontraron cuentas"
},
"loginname": {
"title": "¡Bienvenido de nuevo!",
diff --git a/apps/login/locales/it.json b/apps/login/locales/it.json
index b33afc2070..66ee615a47 100644
--- a/apps/login/locales/it.json
+++ b/apps/login/locales/it.json
@@ -10,7 +10,8 @@
},
"logout": {
"title": "Esci",
- "description": "Seleziona l'account che desideri uscire"
+ "description": "Seleziona l'account che desideri uscire",
+ "noResults": "Nessun account trovato"
},
"loginname": {
"title": "Bentornato!",
diff --git a/apps/login/locales/pl.json b/apps/login/locales/pl.json
index a0ce3405c1..cc65198800 100644
--- a/apps/login/locales/pl.json
+++ b/apps/login/locales/pl.json
@@ -10,7 +10,8 @@
},
"logout": {
"title": "Wyloguj się",
- "description": "Wybierz konto, które chcesz usunąć"
+ "description": "Wybierz konto, które chcesz usunąć",
+ "noResults": "Nie znaleziono kont"
},
"loginname": {
"title": "Witamy ponownie!",
diff --git a/apps/login/locales/ru.json b/apps/login/locales/ru.json
index 97f8f7200c..766b956734 100644
--- a/apps/login/locales/ru.json
+++ b/apps/login/locales/ru.json
@@ -10,7 +10,8 @@
},
"logout": {
"title": "Выход",
- "description": "Выберите аккаунт, который хотите удалить"
+ "description": "Выберите аккаунт, который хотите удалить",
+ "noResults": "Аккаунты не найдены"
},
"loginname": {
"title": "С возвращением!",
diff --git a/apps/login/locales/zh.json b/apps/login/locales/zh.json
index 067d027ab6..fb34dfcbe3 100644
--- a/apps/login/locales/zh.json
+++ b/apps/login/locales/zh.json
@@ -10,7 +10,8 @@
},
"logout": {
"title": "注销",
- "description": "选择您想注销的账户"
+ "description": "选择您想注销的账户",
+ "noResults": "未找到账户"
},
"loginname": {
"title": "欢迎回来!",
diff --git a/apps/login/src/app/(login)/logout/page.tsx b/apps/login/src/app/(login)/logout/page.tsx
index b5323a7117..d15ead76a1 100644
--- a/apps/login/src/app/(login)/logout/page.tsx
+++ b/apps/login/src/app/(login)/logout/page.tsx
@@ -1,5 +1,5 @@
import { DynamicTheme } from "@/components/dynamic-theme";
-import { SessionsList } from "@/components/sessions-list";
+import { SessionsClearList } from "@/components/sessions-clear-list";
import { getAllSessionCookieIds } from "@/lib/cookies";
import { getServiceUrlFromHeaders } from "@/lib/service";
import {
@@ -73,7 +73,7 @@ export default async function Page(props: {
{t("description")}
-
+
diff --git a/apps/login/src/components/language-switcher.tsx b/apps/login/src/components/language-switcher.tsx
index 8d68248c4a..67b54e58e3 100644
--- a/apps/login/src/components/language-switcher.tsx
+++ b/apps/login/src/components/language-switcher.tsx
@@ -43,7 +43,7 @@ export function LanguageSwitcher() {
>
{selected.name}
@@ -61,7 +61,7 @@ export function LanguageSwitcher() {
value={lang}
className="group flex cursor-default items-center gap-2 rounded-lg py-1.5 px-3 select-none data-[focus]:bg-black/10 dark:data-[focus]:bg-white/10"
>
-
+
{lang.name}
diff --git a/apps/login/src/components/session-clear-item.tsx b/apps/login/src/components/session-clear-item.tsx
new file mode 100644
index 0000000000..aff303d65f
--- /dev/null
+++ b/apps/login/src/components/session-clear-item.tsx
@@ -0,0 +1,102 @@
+"use client";
+
+import { cleanupSession } from "@/lib/server/session";
+import { timestampDate } from "@zitadel/client";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import moment from "moment";
+import { useLocale, useTranslations } from "next-intl";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { Avatar } from "./avatar";
+import { isSessionValid } from "./session-item";
+
+export function SessionClearItem({
+ session,
+ reload,
+ requestId,
+}: {
+ session: Session;
+ reload: () => void;
+ requestId?: string;
+}) {
+ const t = useTranslations("logout");
+
+ const currentLocale = useLocale();
+ moment.locale(currentLocale === "zh" ? "zh-cn" : currentLocale);
+
+ const [loading, setLoading] = useState(false);
+
+ async function clearSession(id: string) {
+ setLoading(true);
+ const response = await cleanupSession({
+ sessionId: id,
+ })
+ .catch((error) => {
+ setError(error.message);
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ return response;
+ }
+
+ const { valid, verifiedAt } = isSessionValid(session);
+
+ const [error, setError] = useState(null);
+
+ const router = useRouter();
+
+ return (
+
+ );
+}
diff --git a/apps/login/src/components/sessions-clear-list.tsx b/apps/login/src/components/sessions-clear-list.tsx
new file mode 100644
index 0000000000..61288d9bd3
--- /dev/null
+++ b/apps/login/src/components/sessions-clear-list.tsx
@@ -0,0 +1,52 @@
+"use client";
+
+import { timestampDate } from "@zitadel/client";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import { useTranslations } from "next-intl";
+import { useState } from "react";
+import { Alert, AlertType } from "./alert";
+import { SessionClearItem } from "./session-clear-item";
+
+type Props = {
+ sessions: Session[];
+ requestId?: string;
+};
+
+export function SessionsClearList({ sessions, requestId }: Props) {
+ const t = useTranslations("logout");
+ const [list, setList] = useState(sessions);
+ return sessions ? (
+
+ {list
+ .filter((session) => session?.factors?.user?.loginName)
+ // sort by change date descending
+ .sort((a, b) => {
+ const dateA = a.changeDate
+ ? timestampDate(a.changeDate).getTime()
+ : 0;
+ const dateB = b.changeDate
+ ? timestampDate(b.changeDate).getTime()
+ : 0;
+ return dateB - dateA;
+ })
+ // TODO: add sorting to move invalid sessions to the bottom
+ .map((session, index) => {
+ return (
+
{
+ setList(list.filter((s) => s.id !== session.id));
+ }}
+ key={"session-" + index}
+ />
+ );
+ })}
+ {list.length === 0 && (
+ {t("noResults")}
+ )}
+
+ ) : (
+ {t("noResults")}
+ );
+}