From 1f974559619ab27b0f918b7d6c408df34880de38 Mon Sep 17 00:00:00 2001 From: susie-laptop Date: Thu, 8 May 2025 22:18:49 -0400 Subject: [PATCH] [dev]database list --- package-lock.json | 62 ++++++++- package.json | 1 + src/components/DatabaseFilter.jsx | 84 ++++++------ src/components/DatabaseList.jsx | 191 +++++++++++++++++++++++++++- src/components/Sidebar.jsx | 37 ++++-- src/main.jsx | 6 +- src/pages/Brands.jsx | 4 + src/pages/CreatorInbox.jsx | 4 + src/pages/Database.jsx | 4 +- src/router/index.jsx | 17 ++- src/store/index.js | 16 +++ src/store/slices/creatorsSlice.js | 204 ++++++++++++++++++++++++++++++ src/store/slices/filtersSlice.js | 78 ++++++++++++ src/styles/DatabaseFilter.scss | 2 +- src/styles/DatabaseList.scss | 186 +++++++++++++++++++++++++++ src/styles/sidebar.scss | 5 +- 16 files changed, 836 insertions(+), 65 deletions(-) create mode 100644 src/pages/Brands.jsx create mode 100644 src/pages/CreatorInbox.jsx create mode 100644 src/store/index.js create mode 100644 src/store/slices/creatorsSlice.js create mode 100644 src/store/slices/filtersSlice.js create mode 100644 src/styles/DatabaseList.scss diff --git a/package-lock.json b/package-lock.json index 49488fd..8e0045e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@fortawesome/free-regular-svg-icons": "^6.7.2", "@fortawesome/free-solid-svg-icons": "^6.7.2", "@fortawesome/react-fontawesome": "^0.2.2", + "@reduxjs/toolkit": "^2.8.1", "bootstrap": "^5.3.3", "lodash": "^4.17.21", "lucide-react": "^0.508.0", @@ -1396,6 +1397,31 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, + "node_modules/@reduxjs/toolkit": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.8.1.tgz", + "integrity": "sha512-GLjHS13LiBdiuxSJvfWs3+Cx5yt97mCbuVlDteTusS6VRksPhoWviO8L1e3Re1G94m6lkw/l4pjEEyyNaGf19g==", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^10.0.3", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@restart/hooks": { "version": "0.4.16", "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", @@ -1706,6 +1732,16 @@ "win32" ] }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==" + }, "node_modules/@swc/helpers": { "version": "0.5.17", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", @@ -2955,6 +2991,15 @@ "node": ">= 4" } }, + "node_modules/immer": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/immutable": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.2.tgz", @@ -4041,9 +4086,20 @@ "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", - "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "optional": true, - "peer": true + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" }, "node_modules/resolve-from": { "version": "4.0.0", diff --git a/package.json b/package.json index 5fe4802..9c2a805 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@fortawesome/free-regular-svg-icons": "^6.7.2", "@fortawesome/free-solid-svg-icons": "^6.7.2", "@fortawesome/react-fontawesome": "^0.2.2", + "@reduxjs/toolkit": "^2.8.1", "bootstrap": "^5.3.3", "lodash": "^4.17.21", "lucide-react": "^0.508.0", diff --git a/src/components/DatabaseFilter.jsx b/src/components/DatabaseFilter.jsx index 8032029..9e9dd61 100644 --- a/src/components/DatabaseFilter.jsx +++ b/src/components/DatabaseFilter.jsx @@ -1,10 +1,23 @@ import React, { useState, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import { Card, Row, Col, Form, InputGroup, Button } from 'react-bootstrap'; import { Eye } from 'lucide-react'; import RangeSlider from './RangeSlider'; +import { + setCategory, + toggleEcommerceRating, + toggleExposureRating, + toggleGmvRange, + setViewsRange, + resetFilters, +} from '../store/slices/filtersSlice'; +import { fetchCreators } from '../store/slices/creatorsSlice'; import '../styles/DatabaseFilter.scss'; -export default function DatabaseFilter() { +export default function DatabaseFilter({ path }) { + const dispatch = useDispatch(); + const filters = useSelector((state) => state.filters); + // 类别选项数据 const categories = [ 'Phones & Electronics', @@ -50,54 +63,44 @@ export default function DatabaseFilter() { return closestIndex; }; - // 存储选择的状态 - const [selectedCategory, setSelectedCategory] = useState('Homes Supplies'); - const [selectedEcommerceRatings, setSelectedEcommerceRatings] = useState(['L2', 'L3']); - const [selectedExposureRatings, setSelectedExposureRatings] = useState([]); - const [selectedGmvRanges, setSelectedGmvRanges] = useState(['$5k - $25k', '$25k - $60k']); + // 本地状态用于表单控制 + const [minViews, setMinViews] = useState(filters.viewsRange[0]); + const [maxViews, setMaxViews] = useState(filters.viewsRange[1]); - // 视频观看量过滤器数值范围 - 使用离散值初始化 - const [viewsRange, setViewsRange] = useState([0, 100000]); - const [minViews, setMinViews] = useState(0); - const [maxViews, setMaxViews] = useState(100000); + // 监听Redux状态变化,更新本地表单状态 + useEffect(() => { + setMinViews(filters.viewsRange[0]); + setMaxViews(filters.viewsRange[1]); + }, [filters.viewsRange]); + + // 组件加载时获取数据 + useEffect(() => { + dispatch(fetchCreators({ path })); + }, [dispatch, filters]); // 处理类别选择 const handleCategorySelect = (category) => { - setSelectedCategory(category); + dispatch(setCategory(category)); }; // 处理电商评级选择 const handleEcommerceRatingSelect = (rating) => { - if (selectedEcommerceRatings.includes(rating)) { - setSelectedEcommerceRatings(selectedEcommerceRatings.filter((r) => r !== rating)); - } else { - setSelectedEcommerceRatings([...selectedEcommerceRatings, rating]); - } + dispatch(toggleEcommerceRating(rating)); }; // 处理曝光评级选择 const handleExposureRatingSelect = (rating) => { - if (selectedExposureRatings.includes(rating)) { - setSelectedExposureRatings(selectedExposureRatings.filter((r) => r !== rating)); - } else { - setSelectedExposureRatings([...selectedExposureRatings, rating]); - } + dispatch(toggleExposureRating(rating)); }; // 处理GMV范围选择 const handleGmvRangeSelect = (range) => { - if (selectedGmvRanges.includes(range)) { - setSelectedGmvRanges(selectedGmvRanges.filter((r) => r !== range)); - } else { - setSelectedGmvRanges([...selectedGmvRanges, range]); - } + dispatch(toggleGmvRange(range)); }; // 处理视图范围更新 const handleViewsRangeChange = (newRange) => { - setViewsRange(newRange); - setMinViews(newRange[0]); - setMaxViews(newRange[1]); + dispatch(setViewsRange(newRange)); }; // 处理min input变更 @@ -123,7 +126,7 @@ export default function DatabaseFilter() { const finalValue = Math.min(discreteValue, maxViews); setMinViews(finalValue); - setViewsRange([finalValue, viewsRange[1]]); + dispatch(setViewsRange([finalValue, filters.viewsRange[1]])); } else { // 找到最接近的离散值 const closestIndex = findClosestDiscreteIndex(maxViews); @@ -133,7 +136,7 @@ export default function DatabaseFilter() { const finalValue = Math.max(discreteValue, minViews); setMaxViews(finalValue); - setViewsRange([viewsRange[0], finalValue]); + dispatch(setViewsRange([filters.viewsRange[0], finalValue])); } }; @@ -149,7 +152,7 @@ export default function DatabaseFilter() { }; return ( - +

Filter

@@ -160,7 +163,7 @@ export default function DatabaseFilter() { {categories.map((category) => (
- - + + ); } diff --git a/src/router/index.jsx b/src/router/index.jsx index ab85bab..2b85637 100644 --- a/src/router/index.jsx +++ b/src/router/index.jsx @@ -2,6 +2,8 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import Home from '../pages/Home'; import Database from '../pages/Database'; import MainLayout from '../components/MainLayout'; +import Brands from '../pages/Brands'; +import CreatorInbox from '../pages/CreatorInbox'; // Routes configuration object const routes = [ @@ -44,11 +46,20 @@ const routes = [ }, { path: '/brands', - element: , + element: , }, { - path: '/creator-inbox/*', - element: , + path: '/creator-inbox', + children: [ + { + path: '', + element: , + }, + { + path: 'templates', + element: , + }, + ], }, { path: '/settings', diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 0000000..73df1ec --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,16 @@ +import { configureStore } from '@reduxjs/toolkit'; +import creatorsReducer from './slices/creatorsSlice'; +import filtersReducer from './slices/filtersSlice'; + +export const store = configureStore({ + reducer: { + creators: creatorsReducer, + filters: filtersReducer, + }, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ + serializableCheck: false, + }), +}); + +export default store; diff --git a/src/store/slices/creatorsSlice.js b/src/store/slices/creatorsSlice.js new file mode 100644 index 0000000..c4666f7 --- /dev/null +++ b/src/store/slices/creatorsSlice.js @@ -0,0 +1,204 @@ +import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; + +// 模拟创作者数据,实际项目中会从API获取 +const mockCreators = [ + { + id: 1, + name: 'name', + avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=1', + category: 'Phones & Electronics', + ecommerceLevel: 'L2', + exposureLevel: 'KOC-1', + followers: '162.2k', + gmv: '$534.1k', + soldPercentage: '18.1%', + avgViews: '1.9k', + hasEcommerce: true, + hasTiktok: true, + verified: true, + }, + { + id: 2, + name: 'name', + avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=2', + category: 'Womenswear & Underwear', + ecommerceLevel: 'L3', + exposureLevel: 'KOL-3', + followers: '162.2k', + gmv: '$534.1k', + soldPercentage: '18.1%', + avgViews: '1.9k', + hasEcommerce: false, + hasTiktok: true, + verified: false, + }, + { + id: 3, + name: 'name', + avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=3', + category: 'Sports & Outdoor', + ecommerceLevel: 'L4', + exposureLevel: 'KOC-2', + followers: '162.2k', + gmv: '$534.1k', + soldPercentage: '18.1%', + avgViews: '1.9k', + hasEcommerce: true, + hasTiktok: true, + verified: false, + }, + { + id: 4, + name: 'name', + avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=4', + category: 'Food & Beverage', + ecommerceLevel: 'L1', + exposureLevel: 'KOC-2', + followers: '162.2k', + gmv: '$534.1k', + soldPercentage: '18.1%', + avgViews: '1.9k', + hasEcommerce: true, + hasTiktok: true, + hasInstagram: true, + hasYoutube: true, + verified: true, + }, + { + id: 5, + name: 'name', + avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=5', + category: 'Health', + ecommerceLevel: 'L5', + exposureLevel: 'KOL-2', + followers: '162.2k', + gmv: '$534.1k', + soldPercentage: '18.1%', + avgViews: '1.9k', + hasEcommerce: false, + hasTiktok: true, + hasInstagram: true, + hasYoutube: true, + verified: true, + }, + { + id: 6, + name: 'name', + avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=6', + category: 'Kitchenware', + ecommerceLevel: 'New tag', + exposureLevel: 'New tag', + followers: '162.2k', + gmv: '$534.1k', + soldPercentage: '18.1%', + avgViews: '1.9k', + hasEcommerce: true, + hasTiktok: true, + hasInstagram: true, + hasYoutube: true, + verified: false, + }, +]; + +// 模拟API获取数据的异步Thunk +export const fetchCreators = createAsyncThunk('creators/fetchCreators', async ({ path }, { getState }) => { + // 模拟API调用延迟 + await new Promise((resolve) => setTimeout(resolve, 500)); + + // 获取当前的筛选条件 + const state = getState(); + const filters = state.filters; + + // 应用筛选逻辑(实际项目中可能在服务器端进行) + let filteredCreators = [...mockCreators]; + console.log(filters); + + // 如果有选定的类别,进行筛选 + if (filters.category.length > 0) { + filteredCreators = filteredCreators.filter((creator) => filters.category.includes(creator.category)); + } + + // 如果有选定的电商评级,进行筛选 + if (filters.ecommerceRatings.length > 0) { + filteredCreators = filteredCreators.filter((creator) => + filters.ecommerceRatings.includes(creator.ecommerceLevel) + ); + } + + // 如果有选定的曝光评级,进行筛选 + if (filters.exposureRatings.length > 0) { + filteredCreators = filteredCreators.filter((creator) => + filters.exposureRatings.includes(creator.exposureLevel) + ); + } + + // 筛选观看量范围 + if (filters.viewsRange.length === 2) { + const minViews = filters.viewsRange[0]; + const maxViews = filters.viewsRange[1]; + + filteredCreators = filteredCreators.filter((creator) => { + // 将带k的字符串转换为数字 + const viewsStr = creator.avgViews; + let views = parseFloat(viewsStr); + if (viewsStr.includes('k')) { + views *= 1000; + } else if (viewsStr.includes('M')) { + views *= 1000000; + } + + return views >= minViews && views <= maxViews; + }); + } + + return filteredCreators; +}); + +const initialState = { + creators: [], + status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed' + error: null, + selectedCreators: [], +}; + +const creatorsSlice = createSlice({ + name: 'creators', + initialState, + reducers: { + toggleCreatorSelection: (state, action) => { + const creatorId = action.payload; + const isSelected = state.selectedCreators.includes(creatorId); + + if (isSelected) { + state.selectedCreators = state.selectedCreators.filter((id) => id !== creatorId); + } else { + state.selectedCreators.push(creatorId); + } + }, + selectAllCreators: (state) => { + state.selectedCreators = state.creators.map((creator) => creator.id); + }, + clearCreatorSelection: (state) => { + state.selectedCreators = []; + }, + }, + extraReducers: (builder) => { + builder + .addCase(fetchCreators.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchCreators.fulfilled, (state, action) => { + state.status = 'succeeded'; + state.creators = action.payload; + }) + .addCase(fetchCreators.rejected, (state, action) => { + console.log(action); + state.status = 'failed'; + state.error = action.error.message; + }); + }, +}); + +export const { toggleCreatorSelection, selectAllCreators, clearCreatorSelection } = creatorsSlice.actions; + +export default creatorsSlice.reducer; diff --git a/src/store/slices/filtersSlice.js b/src/store/slices/filtersSlice.js new file mode 100644 index 0000000..bd478a0 --- /dev/null +++ b/src/store/slices/filtersSlice.js @@ -0,0 +1,78 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState = { + category: ['Homes Supplies'], + ecommerceRatings: ['L2', 'L3'], + exposureRatings: [], + gmvRanges: ['$5k - $25k', '$25k - $60k'], + viewsRange: [0, 100000], + sortBy: 'followers', + sortDirection: 'desc', +}; + +const filtersSlice = createSlice({ + name: 'filters', + initialState, + reducers: { + setCategory: (state, action) => { + const category = action.payload; + if (state.category.includes(category)) { + state.category = state.category.filter((r) => r !== category); + } else { + state.category.push(category); + } + }, + toggleEcommerceRating: (state, action) => { + const rating = action.payload; + if (state.ecommerceRatings.includes(rating)) { + state.ecommerceRatings = state.ecommerceRatings.filter((r) => r !== rating); + } else { + state.ecommerceRatings.push(rating); + } + }, + toggleExposureRating: (state, action) => { + const rating = action.payload; + if (state.exposureRatings.includes(rating)) { + state.exposureRatings = state.exposureRatings.filter((r) => r !== rating); + } else { + state.exposureRatings.push(rating); + } + }, + toggleGmvRange: (state, action) => { + const range = action.payload; + if (state.gmvRanges.includes(range)) { + state.gmvRanges = state.gmvRanges.filter((r) => r !== range); + } else { + state.gmvRanges.push(range); + } + }, + setViewsRange: (state, action) => { + state.viewsRange = action.payload; + }, + setSortBy: (state, action) => { + // 如果选择了当前已激活的排序项,则切换排序方向 + if (state.sortBy === action.payload) { + state.sortDirection = state.sortDirection === 'asc' ? 'desc' : 'asc'; + } else { + // 否则设置新的排序项,并重置排序方向为降序 + state.sortBy = action.payload; + state.sortDirection = 'desc'; + } + }, + resetFilters: () => { + return initialState; + }, + }, +}); + +export const { + setCategory, + toggleEcommerceRating, + toggleExposureRating, + toggleGmvRange, + setViewsRange, + setSortBy, + resetFilters, +} = filtersSlice.actions; + +export default filtersSlice.reducer; diff --git a/src/styles/DatabaseFilter.scss b/src/styles/DatabaseFilter.scss index 57c9a90..f0395e5 100644 --- a/src/styles/DatabaseFilter.scss +++ b/src/styles/DatabaseFilter.scss @@ -17,7 +17,7 @@ .filter-title { width: 170px; margin: 0; - color: $gray-500; + color: $gray-700; } .filter-options { diff --git a/src/styles/DatabaseList.scss b/src/styles/DatabaseList.scss new file mode 100644 index 0000000..0b4ebbd --- /dev/null +++ b/src/styles/DatabaseList.scss @@ -0,0 +1,186 @@ +@import './custom-theme.scss'; + +.creator-database-table { + .table { + border-collapse: separate; + border-spacing: 0; + + th { + background-color: #f8f9fa; + border-top: none; + border-bottom: 1px solid #dee2e6; + padding: 1rem 0.75rem; + font-weight: 600; + font-size: 0.875rem; + color: #495057; + cursor: pointer; + position: relative; + vertical-align: middle; + + &:first-child { + border-top-left-radius: 0.5rem; + } + + &:last-child { + border-top-right-radius: 0.5rem; + } + + &:hover { + background-color: #f1f3f5; + } + } + + td { + padding: 0.75rem; + vertical-align: middle; + border-bottom: 1px solid #f1f3f4; + font-size: 0.875rem; + } + + tbody tr { + &:hover { + background-color: rgba(0, 0, 0, 0.02); + } + + &.selected { + background-color: rgba(99, 102, 241, 0.05); + + &:hover { + background-color: rgba(99, 102, 241, 0.1); + } + } + + &:last-child td { + border-bottom: none; + } + } + } + + .creator-cell { + .creator-avatar { + position: relative; + width: 36px; + height: 36px; + margin-right: 12px; + + img { + width: 100%; + height: 100%; + border-radius: 50%; + object-fit: cover; + border: 2px solid #e2e8f0; + } + + .verified-badge { + position: absolute; + bottom: 0; + right: 0; + background-color: #10b981; + color: white; + border-radius: 50%; + width: 10px; + height: 10px; + font-size: 8px; + display: flex; + align-items: center; + justify-content: center; + } + } + + .creator-name { + font-weight: 500; + } + } + + .category-pill { + display: inline-block; + padding: 0.25rem 0.75rem; + border-radius: 1rem; + background-color: #f8f9fa; + font-size: 0.75rem; + color: #495057; + white-space: nowrap; + + &.phones { + background-color: rgba(99, 102, 241, 0.1); + color: #6366f1; + } + + &.women { + background-color: rgba(244, 114, 182, 0.1); + color: #f472b6; + } + + &.sports { + background-color: rgba(52, 211, 153, 0.1); + color: #34d399; + } + + &.food { + background-color: rgba(251, 146, 60, 0.1); + color: #fb923c; + } + + &.health { + background-color: rgba(56, 189, 248, 0.1); + color: #38bdf8; + } + + &.kitchen { + background-color: rgba(168, 85, 247, 0.1); + color: #a855f7; + } + } + + .level-badge { + display: inline-block; + padding: 0.25rem 0.75rem; + border-radius: 1rem; + font-size: 0.75rem; + white-space: nowrap; + + &.ecommerce-level { + background-color: rgba(99, 102, 241, 0.1); + color: #6366f1; + } + + &.exposure-level { + background-color: rgba(14, 165, 233, 0.1); + color: #0ea5e9; + + &[data-level^="KOC"] { + background-color: rgba(52, 211, 153, 0.1); + color: #34d399; + } + + &[data-level^="KOL"] { + background-color: rgba(251, 113, 133, 0.1); + color: #fb7185; + } + } + } + + .colored-dot { + width: 8px; + height: 8px; + border-radius: 50%; + display: inline-block; + + &.blue { + background-color: #6366f1; + } + } + + .social-icon { + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + + &.tiktok-icon { + font-size: 14px; + color: #000000; + } + } +} \ No newline at end of file diff --git a/src/styles/sidebar.scss b/src/styles/sidebar.scss index 36627b6..8bc5606 100644 --- a/src/styles/sidebar.scss +++ b/src/styles/sidebar.scss @@ -28,7 +28,7 @@ align-items: center; text-decoration: none; font-weight: 500; - + border-left: 4px solid transparent; &:hover { text-decoration: none; color: var(--bs-primary); @@ -64,7 +64,8 @@ font-size: 1rem; box-shadow: none; font-weight: 500; - + border-left: 4px solid transparent; + &:not(.collapsed) { color: var(--bs-primary); background-color: rgba(var(--bs-primary-rgb), 0.05);