diff --git a/components/PlayerSanctions.vue b/components/PlayerSanctions.vue index e78f07cf7..8ed8e6370 100644 --- a/components/PlayerSanctions.vue +++ b/components/PlayerSanctions.vue @@ -4,7 +4,6 @@ import { Edit2, Trash2, Calendar as CalendarIcon, - AlertTriangle, ExternalLink, Ban, MicOff, @@ -15,6 +14,7 @@ import { ShieldAlert, ShieldCheck, ShieldX, + TriangleAlert, } from "lucide-vue-next"; import { Button } from "~/components/ui/button"; import { @@ -84,7 +84,7 @@ const tabCountClasses = 'text-destructive hover:text-destructive': activeSanctions > 0, }" > - + {{ $t("player.sanctions.title") }} + - {{ sanction.type }} + {{ typeLabel(sanction.type) }} - {{ - isExpired(sanction) - ? $t("player.sanctions.expired") - : $t("player.sanctions.active") - }} + {{ statusLabel(sanction) }} - + @@ -335,6 +337,7 @@ const tabCountClasses = variant="ghost" size="icon" class="h-7 w-7 text-destructive hover:text-destructive" + :aria-label="$t('player.sanctions.remove')" @click="removeSanction(sanction)" > @@ -356,6 +359,7 @@ const tabCountClasses = { + if (this.isWarning(sanction)) { + return false; + } if (sanction.remove_sanction_date) { return new Date(sanction.remove_sanction_date) > new Date(); } @@ -855,12 +862,33 @@ export default { }, }, methods: { + isWarning(sanction: any) { + return sanction.type === "warning"; + }, isExpired(sanction: any) { + if (this.isWarning(sanction)) { + return false; + } return ( !!sanction.remove_sanction_date && new Date(sanction.remove_sanction_date) <= new Date() ); }, + canEditEndDate(sanction: any) { + return !this.isWarning(sanction) && !this.isExpired(sanction); + }, + typeLabel(type: string) { + const key = `player.sanctions.type_labels.${type}`; + return this.$te(key) ? this.$t(key) : type; + }, + statusLabel(sanction: any) { + if (this.isWarning(sanction)) { + return this.$t("player.sanctions.on_record"); + } + return this.isExpired(sanction) + ? this.$t("player.sanctions.expired") + : this.$t("player.sanctions.active"); + }, isAutoSteamBan(sanction: any) { return ( sanction.type === "ban" && @@ -881,12 +909,15 @@ export default { : "text-[hsl(var(--tac-amber))]"; }, statusPillClass(sanction: any) { - return this.isExpired(sanction) - ? "border-border text-muted-foreground" - : "border-destructive/40 bg-destructive/10 text-destructive"; + if (this.isExpired(sanction)) { + return "border-border text-muted-foreground"; + } + return sanction.type === "ban" + ? "border-destructive/40 bg-destructive/10 text-destructive" + : "border-[hsl(var(--tac-amber)/0.5)] bg-[hsl(var(--tac-amber)/0.12)] text-[hsl(var(--tac-amber))]"; }, openEditDialog(sanction: any) { - if (this.isExpired(sanction)) { + if (!this.canEditEndDate(sanction)) { return; } this.editingSanction = sanction; @@ -991,11 +1022,13 @@ export default { $serverId: String $steam_id: String! $type: String! + $sanction_id: uuid ) { unsanctionServerPlayer( serverId: $serverId steam_id: $steam_id type: $type + sanction_id: $sanction_id ) { enforced message @@ -1006,6 +1039,7 @@ export default { serverId: this.serverId ?? null, steam_id: this.playerId, type: this.sanctionToDelete.type, + sanction_id: this.sanctionToDelete.id, }, }); diff --git a/components/SanctionPlayer.vue b/components/SanctionPlayer.vue index d10eef313..6d4220b29 100644 --- a/components/SanctionPlayer.vue +++ b/components/SanctionPlayer.vue @@ -72,7 +72,9 @@ import SettingHeader from "~/components/match/SettingHeader.vue"; > - {{ type }} + + {{ $t(`player.sanctions.type_labels.${type}`) }} + {{ sanction.description }} @@ -103,16 +105,24 @@ import SettingHeader from "~/components/match/SettingHeader.vue"; {{ sanctions[sanctionType].description }} + + {{ $t("player.sanction.warning_note") }} + {{ $t("pages.dedicated_servers.detail.sanctions_wip") }} @@ -127,7 +137,7 @@ import SettingHeader from "~/components/match/SettingHeader.vue"; - + {{ $t("player.sanction.reason_label") }} - + {{ $t("player.sanctions.duration_label") }} @@ -205,7 +219,7 @@ export default { form: useForm({ validationSchema: toTypedSchema( z.object({ - reason: z.string().min(1), + reason: z.string().optional(), duration: z.string().min(1), }), ), @@ -215,6 +229,9 @@ export default { }; }, computed: { + isWarning(): boolean { + return this.sanctionType === "warning"; + }, sanctions(): Record { return { ban: { @@ -233,6 +250,10 @@ export default { icon: BellOff, description: this.$t("player.sanction.types.silence_description"), }, + warning: { + icon: TriangleAlert, + description: this.$t("player.sanction.types.warning_description"), + }, }; }, durations(): Array<{ label: string; duration: number }> { @@ -275,6 +296,14 @@ export default { return; } + if (this.isWarning && !this.form.values.reason?.trim()) { + this.form.setFieldError( + "reason", + this.$t("player.sanction.reason_required"), + ); + return; + } + this.submitting = true; try { await this.$apollo.mutate({ @@ -304,9 +333,10 @@ export default { steam_id: this.player.steam_id, type: this.sanctionType, reason: this.form.values.reason, - duration: this.form.values.duration - ? parseInt(this.form.values.duration) - : 0, + duration: + !this.isWarning && this.form.values.duration + ? parseInt(this.form.values.duration) + : 0, }, }); diff --git a/components/notification/NotificationItem.vue b/components/notification/NotificationItem.vue index 856d8bb32..13c38e18b 100644 --- a/components/notification/NotificationItem.vue +++ b/components/notification/NotificationItem.vue @@ -1,11 +1,16 @@
- + @@ -335,6 +337,7 @@ const tabCountClasses = variant="ghost" size="icon" class="h-7 w-7 text-destructive hover:text-destructive" + :aria-label="$t('player.sanctions.remove')" @click="removeSanction(sanction)" > @@ -356,6 +359,7 @@ const tabCountClasses =
{{ sanction.description }} @@ -103,16 +105,24 @@ import SettingHeader from "~/components/match/SettingHeader.vue"; {{ sanctions[sanctionType].description }} +
+ {{ $t("player.sanction.warning_note") }} +