From 43702e8bf7a2b6827473a2ef177eee36592e0416 Mon Sep 17 00:00:00 2001 From: lethdat Date: Fri, 27 Mar 2026 00:35:53 +0700 Subject: [PATCH] refactor: update icon components to use CSS variables for fill colors - Changed fill attributes in Upload, Video, VideoPlayIcon, hard-drive, and shield-user icons to use CSS variables for better theming. - Removed index.ts file from icons directory as it was no longer needed. - Updated AppButton component to support new icon sizes. - Modified AdsVastTable to use icon buttons with updated filled icons. - Replaced inline SVGs with icon components in NotificationSettings, SecurityAccountStatusRow, SecurityChangePasswordRow, SecurityEmailRow, SecurityLanguageRow, SecurityLogoutRow, and SecurityTelegramRow for consistency and maintainability. - Added new CSS variables for fill colors in uno.config.ts. --- proto/v1/common.proto | 46 ------ proto/v1/user.proto | 134 ------------------ public/locales/en/translation.json | 14 +- public/locales/vi/translation.json | 14 +- src/components/DashboardNav.vue | 53 ++++--- src/components/icons/AdvertisementIcon.vue | 2 +- src/components/icons/Bell.vue | 2 +- src/components/icons/BellIcon.vue | 12 -- src/components/icons/Chart.vue | 2 +- src/components/icons/CoinsIcon.vue | 2 +- src/components/icons/Credit.vue | 2 +- src/components/icons/Globe.vue | 4 +- src/components/icons/Home.vue | 2 +- src/components/icons/LanguageIcon.vue | 16 +-- src/components/icons/ListIcon.vue | 2 +- src/components/icons/MailIcon.vue | 3 +- src/components/icons/MoneyCheck.vue | 2 +- src/components/icons/PencilIcon.vue | 11 +- src/components/icons/SendIcon.vue | 3 +- src/components/icons/SettingsIcon.vue | 2 +- src/components/icons/TrashIcon.vue | 5 +- src/components/icons/Upload.vue | 2 +- src/components/icons/Video.vue | 2 +- src/components/icons/VideoPlayIcon.vue | 2 +- src/components/icons/hard-drive.vue | 2 +- src/components/icons/index.ts | 42 ------ src/components/icons/shield-user.vue | 2 +- src/components/ui/AppButton.vue | 5 +- .../AdsVast/components/AdsVastTable.tsx | 8 +- .../NotificationSettings.vue | 5 +- .../components/SecurityAccountStatusRow.vue | 6 +- .../components/SecurityChangePasswordRow.vue | 5 +- .../components/SecurityEmailRow.vue | 7 +- .../components/SecurityLanguageRow.vue | 4 +- .../components/SecurityLogoutRow.vue | 3 +- .../components/SecurityTelegramRow.vue | 1 + uno.config.ts | 4 + 37 files changed, 100 insertions(+), 333 deletions(-) delete mode 100644 proto/v1/common.proto delete mode 100644 proto/v1/user.proto delete mode 100644 src/components/icons/BellIcon.vue delete mode 100644 src/components/icons/index.ts diff --git a/proto/v1/common.proto b/proto/v1/common.proto deleted file mode 100644 index 3e3eda4..0000000 --- a/proto/v1/common.proto +++ /dev/null @@ -1,46 +0,0 @@ -syntax = "proto3"; - -package stream.common.v1; - -option go_package = "stream/proto/gen/go/common/v1;commonv1"; - -import "google/protobuf/timestamp.proto"; - -message RequestContext { - string user_id = 1; - string email = 2; - string role = 3; - string request_id = 4; - string source = 5; -} - -message PaginationRequest { - int32 page = 1; - int32 page_size = 2; -} - -message PaginationResponse { - int32 page = 1; - int32 page_size = 2; - int64 total = 3; -} - -message Money { - double amount = 1; - string currency = 2; -} - -message Empty {} - -message IdRequest { - string id = 1; -} - -message DeleteResponse { - string message = 1; -} - -message TimestampRange { - google.protobuf.Timestamp from = 1; - google.protobuf.Timestamp to = 2; -} diff --git a/proto/v1/user.proto b/proto/v1/user.proto deleted file mode 100644 index bad3eb9..0000000 --- a/proto/v1/user.proto +++ /dev/null @@ -1,134 +0,0 @@ -syntax = "proto3"; - -package stream.User.v1; - -option go_package = "stream/proto/gen/go/User/v1;Userv1"; -import "google/protobuf/empty.proto"; -import "google/protobuf/timestamp.proto"; - -service UserService { - // User CRUD - rpc GetUser(GetUserRequest) returns (GetUserResponse); - rpc GetUserByEmail(GetUserByEmailRequest) returns (GetUserResponse); - rpc ListUsers(ListUsersRequest) returns (ListUsersResponse); - rpc CreateUser(CreateUserRequest) returns (CreateUserResponse); - rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse); - rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse); - rpc UpdateUserPassword(UpdateUserPasswordRequest) returns (google.protobuf.Empty); - - // Preferences - rpc GetPreferences(GetPreferencesRequest) returns (GetPreferencesResponse); - rpc UpsertPreferences(UpsertPreferencesRequest) returns (UpsertPreferencesResponse); -} - -// ─── User Messages ─────────────────────────────────────────────────────────── -message UpdateUserPasswordRequest { - string id = 1; - string new_password = 2; -} -message GetUserRequest { - string id = 1; -} - -message GetUserByEmailRequest { - string email = 1; -} - -message GetUserResponse { - User user = 1; -} - -message ListUsersRequest { - int32 page = 1; - int32 page_size = 2; - string role = 3; // optional filter -} - -message ListUsersResponse { - repeated User users = 1; - int32 total = 2; - int32 page = 3; - int32 page_size = 4; -} - -message CreateUserRequest { - string email = 1; - optional string username = 2; - optional string password = 3; -} - -message CreateUserResponse { - User user = 1; -} - -message UpdateUserRequest { - string id = 1; - optional string username = 2; - optional string avatar = 3; - optional string role = 4; - optional string plan_id = 5; -} - -message UpdateUserResponse { - User user = 1; -} - -message DeleteUserRequest { - string id = 1; -} - -message DeleteUserResponse { - bool success = 1; -} - -// ─── Preferences Messages ──────────────────────────────────────────────────── - -message GetPreferencesRequest { - string user_id = 1; -} - -message GetPreferencesResponse { - Preferences preferences = 1; -} - -message UpsertPreferencesRequest { - Preferences preferences = 1; -} - -message UpsertPreferencesResponse { - Preferences preferences = 1; -} - -// ─── Core Models ───────────────────────────────────────────────────────────── - -message User { - string id = 1; - string email = 2; - string password = 3; - optional string username = 4; - optional string avatar = 5; - optional string role = 6; - optional string google_id = 7; - int64 storage_used = 8; - optional string plan_id = 9; - optional google.protobuf.Timestamp created_at = 10; - google.protobuf.Timestamp updated_at = 11; -} - -message Preferences { - string user_id = 1; - optional string language = 2; - optional string locale = 3; - optional bool email_notifications = 4; - optional bool push_notifications = 5; - optional bool marketing_notifications = 6; - optional bool telegram_notifications = 7; - optional bool autoplay = 8; - optional bool loop = 9; - optional bool muted = 10; - optional bool show_controls = 11; - optional bool pip = 12; - optional bool airplay = 13; - optional bool chromecast = 14; - optional bool encrytion_m3u8 = 15; -} diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index b253f70..3d870a3 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -394,7 +394,7 @@ "create": "Create" }, "confirm": { - "deleteMessage": "Are you sure you want to delete \"{name}\"?", + "deleteMessage": "Are you sure you want to delete \"{{name}}\"?", "deleteHeader": "Delete Config", "deleteAccept": "Delete", "deleteReject": "Cancel" @@ -409,14 +409,14 @@ "enabledSummary": "Config enabled", "disabledSummary": "Config disabled", "defaultUpdatedSummary": "Default updated", - "defaultUpdatedDetail": "{name} is now the default config for new videos.", + "defaultUpdatedDetail": "{{name}} is now the default config for new videos.", "upgradeRequiredSummary": "Config limit reached", "upgradeRequiredDetail": "Free accounts can only have 1 player config.", "limitSummary": "Config limit reached", "limitDetail": "Free accounts can only have 1 player config.", "reconciliationSummary": "Delete extra configs", "reconciliationDetail": "Delete extra player configs until only 1 remains to continue managing them on the free plan.", - "toggleDetail": "{name} has been {state}.", + "toggleDetail": "{{name}} has been {state}.", "deletedSummary": "Config deleted", "deletedDetail": "Player config has been removed.", "failedSummary": "Action failed", @@ -468,7 +468,7 @@ "create": "Create" }, "confirm": { - "deleteMessage": "Are you sure you want to delete \"{name}\"?", + "deleteMessage": "Are you sure you want to delete \"{{name}}\"?", "deleteHeader": "Delete Template", "deleteAccept": "Delete", "deleteReject": "Cancel" @@ -489,10 +489,10 @@ "enabledSummary": "Template Enabled", "disabledSummary": "Template Disabled", "defaultUpdatedSummary": "Default Updated", - "defaultUpdatedDetail": "{name} is now the default template for new videos.", + "defaultUpdatedDetail": "{{name}} is now the default template for new videos.", "upgradeRequiredSummary": "Upgrade required", "upgradeRequiredDetail": "Upgrade your plan to manage Ads & VAST.", - "toggleDetail": "{name} has been {state}.", + "toggleDetail": "{{name}} has been {state}.", "deletedSummary": "Template Deleted", "deletedDetail": "VAST template has been removed.", "copiedSummary": "Copied", @@ -768,7 +768,7 @@ }, "overview": { "welcome": { - "title": "Hello, {{name}}", + "title": "Hello, {{{name}}}", "subtitle": "Here's what's happening with your content today." }, "stats": { diff --git a/public/locales/vi/translation.json b/public/locales/vi/translation.json index 8d5c718..36f58d9 100644 --- a/public/locales/vi/translation.json +++ b/public/locales/vi/translation.json @@ -394,7 +394,7 @@ "create": "Tạo" }, "confirm": { - "deleteMessage": "Bạn có chắc muốn xóa \"{name}\"?", + "deleteMessage": "Bạn có chắc muốn xóa \"{{name}}\"?", "deleteHeader": "Xóa cấu hình", "deleteAccept": "Xóa", "deleteReject": "Hủy" @@ -409,14 +409,14 @@ "enabledSummary": "Đã bật cấu hình", "disabledSummary": "Đã tắt cấu hình", "defaultUpdatedSummary": "Đã cập nhật mặc định", - "defaultUpdatedDetail": "{name} hiện là cấu hình mặc định cho video mới.", + "defaultUpdatedDetail": "{{name}} hiện là cấu hình mặc định cho video mới.", "upgradeRequiredSummary": "Đã đạt giới hạn cấu hình", "upgradeRequiredDetail": "Tài khoản free chỉ có thể có 1 player config.", "limitSummary": "Đã đạt giới hạn cấu hình", "limitDetail": "Tài khoản free chỉ có thể có 1 player config.", "reconciliationSummary": "Hãy xóa bớt config", "reconciliationDetail": "Hãy xóa các player config dư cho đến khi chỉ còn 1 config để tiếp tục quản lý trên gói free.", - "toggleDetail": "{name} đã được {state}.", + "toggleDetail": "{{name}} đã được {state}.", "deletedSummary": "Đã xóa cấu hình", "deletedDetail": "Cấu hình trình phát đã được gỡ bỏ.", "failedSummary": "Thao tác thất bại", @@ -468,7 +468,7 @@ "create": "Tạo" }, "confirm": { - "deleteMessage": "Bạn có chắc muốn xóa \"{name}\"?", + "deleteMessage": "Bạn có chắc muốn xóa \"{{name}}\"?", "deleteHeader": "Xóa mẫu", "deleteAccept": "Xóa", "deleteReject": "Hủy" @@ -489,10 +489,10 @@ "enabledSummary": "Đã bật mẫu", "disabledSummary": "Đã tắt mẫu", "defaultUpdatedSummary": "Đã cập nhật mặc định", - "defaultUpdatedDetail": "{name} hiện là mẫu mặc định cho video mới.", + "defaultUpdatedDetail": "{{name}} hiện là mẫu mặc định cho video mới.", "upgradeRequiredSummary": "Cần nâng cấp gói", "upgradeRequiredDetail": "Hãy nâng cấp gói để quản lý Ads & VAST.", - "toggleDetail": "{name} đã được {state}.", + "toggleDetail": "{{name}} đã được {state}.", "deletedSummary": "Đã xóa mẫu", "deletedDetail": "Mẫu VAST đã được gỡ bỏ.", "copiedSummary": "Đã sao chép", @@ -767,7 +767,7 @@ }, "overview": { "welcome": { - "title": "Xin chào, {{name}}", + "title": "Xin chào, {{{name}}}", "subtitle": "Đây là tình hình nội dung của bạn hôm nay." }, "stats": { diff --git a/src/components/DashboardNav.vue b/src/components/DashboardNav.vue index bce3bef..ee20e4b 100644 --- a/src/components/DashboardNav.vue +++ b/src/components/DashboardNav.vue @@ -4,9 +4,10 @@ import Home from "@/components/icons/Home.vue"; import SettingsIcon from "@/components/icons/SettingsIcon.vue"; import Video from "@/components/icons/Video.vue"; import { cn } from "@/lib/utils"; +import { useNotifications } from "@/composables/useNotifications"; import { useAuthStore } from "@/stores/auth"; import { useTranslation } from "i18next-vue"; -import { computed, createStaticVNode, ref } from "vue"; +import { computed, createStaticVNode, h, ref } from "vue"; import NotificationDrawer from "./NotificationDrawer.vue"; const className = ":uno: w-12 h-12 p-2 rounded-2xl hover:bg-primary/15 flex press-animated items-center justify-center shrink-0"; @@ -15,6 +16,8 @@ const notificationPopover = ref>(); const isNotificationOpen = ref(false); const { t } = useTranslation(); const auth = useAuthStore(); +const notificationStore = useNotifications(); +const unreadCount = computed(() => notificationStore.unreadCount.value); const isAdmin = computed(() => String(auth.user?.role || "").toLowerCase() === "admin"); @@ -24,10 +27,20 @@ const handleNotificationClick = (event: Event) => { const links = computed>(() => { const baseLinks = [ - { href: "/#home", label: "app", icon: homeHoist, action: () => {}, className }, - { href: "/", label: t("nav.overview"), icon: Home, action: null, className }, - { href: "/videos", label: t("nav.videos"), icon: Video, action: null, className }, { + id: "home", + href: "/#home", label: "app", icon: homeHoist, action: () => { }, className + }, + { + id: "overview", + href: "/", label: t("nav.overview"), icon: Home, action: null, className + }, + { + id: "videos", + href: "/videos", label: t("nav.videos"), icon: Video, action: null, className + }, + { + id: "notification", href: "/notification", label: t("nav.notification"), icon: Bell, @@ -37,8 +50,14 @@ const links = computed>(() => { ), action: handleNotificationClick, isActive: isNotificationOpen, + expandComponent: unreadCount.value > 0 ? () => h('span', { + class: 'absolute -top-2 -right-2 min-w-4 h-4 text-xs font-bold text-white bg-red rounded-full flex items-center justify-center' + }, [unreadCount.value > 9 ? '9+' : unreadCount.value]) : undefined + }, + { + id: "settings", + href: "/settings", label: t("nav.settings"), icon: SettingsIcon, action: null, className }, - { href: "/settings", label: t("nav.settings"), icon: SettingsIcon, action: null, className }, ] as const; return baseLinks; }); @@ -46,24 +65,18 @@ const links = computed>(() => {