proje-react-panel 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules.md +11 -1
- package/.vscode/settings.json +1 -1
- package/README.md +57 -1
- package/dist/__tests__/components/form/CustomField.test.d.ts +4 -0
- package/dist/api/ApiConfig.d.ts +11 -0
- package/dist/api/AuthApi.d.ts +2 -5
- package/dist/api/CrudApi.d.ts +11 -12
- package/dist/components/DashboardContainer.d.ts +7 -0
- package/dist/components/DashboardGrid.d.ts +9 -0
- package/dist/components/DashboardItem.d.ts +10 -0
- package/dist/components/ThemeSwitcher.d.ts +7 -0
- package/dist/components/dashboard/Dashboard.d.ts +7 -0
- package/dist/components/dashboard/DashboardGrid.d.ts +7 -0
- package/dist/components/dashboard/DashboardItem.d.ts +6 -0
- package/dist/components/dashboard/index.d.ts +3 -0
- package/dist/components/form/CustomField.d.ts +9 -0
- package/dist/decorators/auth/DefaultLoginForm.d.ts +4 -0
- package/dist/decorators/form/Input.d.ts +1 -1
- package/dist/decorators/form/inputs/CustomInput.d.ts +16 -0
- package/dist/index.cjs.js +15 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.esm.js +15 -1
- package/dist/store/themeStore.d.ts +23 -0
- package/dist/types/Login.d.ts +8 -0
- package/guides/AUTH_LAYOUT_EXAMPLE.md +343 -0
- package/guides/AUTH_LAYOUT_GUIDE.md +819 -0
- package/guides/COLOR_SYSTEM_GUIDE.md +296 -0
- package/guides/DASHBOARD_GUIDE.md +531 -0
- package/guides/IMPLEMENTATION_GUIDE.md +899 -0
- package/guides/USAGE.md +85 -0
- package/how_to.md +184 -0
- package/package.json +7 -1
- package/src/__tests__/components/form/CustomField.test.tsx +92 -0
- package/src/api/ApiConfig.ts +63 -0
- package/src/api/AuthApi.ts +8 -0
- package/src/api/CrudApi.ts +96 -60
- package/src/components/dashboard/Dashboard.tsx +11 -0
- package/src/components/dashboard/DashboardGrid.tsx +14 -0
- package/src/components/dashboard/DashboardItem.tsx +9 -0
- package/src/components/dashboard/index.ts +3 -0
- package/src/components/form/CustomField.tsx +34 -0
- package/src/components/form/FormField.tsx +5 -1
- package/src/components/layout/Layout.tsx +18 -2
- package/src/components/list/ListPage.tsx +1 -1
- package/src/decorators/auth/DefaultLoginForm.ts +32 -0
- package/src/decorators/form/Input.ts +1 -1
- package/src/decorators/form/inputs/CustomInput.ts +26 -0
- package/src/index.ts +31 -0
- package/src/styles/base/_variables.scss +45 -0
- package/src/styles/components/button.scss +3 -3
- package/src/styles/components/checkbox.scss +6 -6
- package/src/styles/components/form-header.scss +21 -19
- package/src/styles/components/uploader.scss +15 -37
- package/src/styles/counter.scss +25 -33
- package/src/styles/dashboard.scss +9 -0
- package/src/styles/details.scss +6 -15
- package/src/styles/error-boundary.scss +75 -74
- package/src/styles/filter-popup.scss +29 -27
- package/src/styles/form.scss +16 -15
- package/src/styles/index.scss +8 -4
- package/src/styles/layout.scss +9 -8
- package/src/styles/list.scss +29 -27
- package/src/styles/loading-screen.scss +4 -4
- package/src/styles/login.scss +3 -3
- package/src/styles/pagination.scss +13 -13
- package/src/styles/sidebar.scss +24 -22
- package/src/styles/utils/scrollbar.scss +4 -3
- package/src/types/Login.ts +9 -0
- package/src/utils/logout.ts +2 -0
- package/dist/components/components/Checkbox.d.ts +0 -7
- package/dist/components/components/Counter.d.ts +0 -9
- package/dist/components/components/ErrorBoundary.d.ts +0 -16
- package/dist/components/components/ErrorComponent.d.ts +0 -4
- package/dist/components/components/FormField.d.ts +0 -17
- package/dist/components/components/ImageUploader.d.ts +0 -15
- package/dist/components/components/InnerForm.d.ts +0 -17
- package/dist/components/components/Label.d.ts +0 -9
- package/dist/components/components/LoadingScreen.d.ts +0 -2
- package/dist/components/components/Uploader.d.ts +0 -8
- package/dist/components/components/index.d.ts +0 -8
- package/dist/components/components/list/Datagrid.d.ts +0 -9
- package/dist/components/components/list/EmptyList.d.ts +0 -2
- package/dist/components/components/list/FilterPopup.d.ts +0 -11
- package/dist/components/components/list/ListPage.d.ts +0 -20
- package/dist/components/components/list/Pagination.d.ts +0 -11
- package/dist/components/components/list/index.d.ts +0 -0
- package/dist/components/pages/ControllerDetails.d.ts +0 -5
- package/dist/components/pages/FormPage.d.ts +0 -18
- package/dist/components/pages/ListPage.d.ts +0 -18
- package/dist/components/pages/Login.d.ts +0 -13
- package/dist/decorators/Crud.d.ts +0 -6
- package/dist/decorators/form/FormOptions.d.ts +0 -7
- package/dist/decorators/form/getFormFields.d.ts +0 -3
- package/dist/decorators/list/GetCellFields.d.ts +0 -2
- package/dist/decorators/list/ImageCell.d.ts +0 -6
- package/dist/decorators/list/ListData.d.ts +0 -6
- package/dist/decorators/list/getListFields.d.ts +0 -2
- package/dist/initPanel.d.ts +0 -2
- package/dist/types/Screen.d.ts +0 -4
- package/dist/types/ScreenCreatorData.d.ts +0 -13
- package/dist/types/getDetailsData.d.ts +0 -1
- package/dist/types/initPanelOptions.d.ts +0 -2
- package/dist/utils/createScreens.d.ts +0 -1
- package/dist/utils/getFields.d.ts +0 -3
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { InputConfiguration } from '../../decorators/form/Input';
|
|
3
|
+
import { useFormContext, Controller } from 'react-hook-form';
|
|
4
|
+
import { CustomInputConfiguration } from '../../decorators/form/inputs/CustomInput';
|
|
5
|
+
|
|
6
|
+
interface CustomFieldProps {
|
|
7
|
+
input: InputConfiguration;
|
|
8
|
+
fieldName: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function CustomField({ input, fieldName, error }: CustomFieldProps) {
|
|
13
|
+
const inputCustom = input as CustomInputConfiguration;
|
|
14
|
+
const { control } = useFormContext();
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Controller
|
|
18
|
+
name={fieldName}
|
|
19
|
+
control={control}
|
|
20
|
+
render={({ field }) => {
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
{inputCustom.render({
|
|
24
|
+
fieldName,
|
|
25
|
+
value: field.value,
|
|
26
|
+
onChange: field.onChange,
|
|
27
|
+
error,
|
|
28
|
+
})}
|
|
29
|
+
</>
|
|
30
|
+
);
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -5,6 +5,7 @@ import { Uploader } from './Uploader';
|
|
|
5
5
|
import { Checkbox } from './Checkbox';
|
|
6
6
|
import { Label } from './Label';
|
|
7
7
|
import { Select } from './Select';
|
|
8
|
+
import { CustomField } from './CustomField';
|
|
8
9
|
|
|
9
10
|
interface FormFieldProps {
|
|
10
11
|
input: InputConfiguration;
|
|
@@ -82,6 +83,8 @@ export function FormField({ input, register, error, baseName }: FormFieldProps)
|
|
|
82
83
|
return <textarea {...register(fieldName)} placeholder={input.placeholder} />;
|
|
83
84
|
case 'select':
|
|
84
85
|
return <Select input={input} fieldName={fieldName} />;
|
|
86
|
+
case 'custom':
|
|
87
|
+
return <CustomField input={input} fieldName={fieldName} error={error?.message} />;
|
|
85
88
|
case 'input': {
|
|
86
89
|
return (
|
|
87
90
|
<input type={input.inputType} {...register(fieldName)} placeholder={input.placeholder} />
|
|
@@ -98,7 +101,8 @@ export function FormField({ input, register, error, baseName }: FormFieldProps)
|
|
|
98
101
|
default:
|
|
99
102
|
return null;
|
|
100
103
|
}
|
|
101
|
-
|
|
104
|
+
// NOTE: error message is a dependency because 'custom' forwards it into the render prop.
|
|
105
|
+
}, [input, register, fieldName, error?.message]);
|
|
102
106
|
|
|
103
107
|
return (
|
|
104
108
|
<div className={`form-field ${input.type === 'nested' ? 'nested-form-field' : ''}`}>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
2
|
import { SideBar } from './SideBar';
|
|
3
|
+
import { LoadingScreen } from '../LoadingScreen';
|
|
3
4
|
import { useAppStore } from '../../store/store';
|
|
4
5
|
|
|
5
6
|
export function Layout<IconType>({
|
|
@@ -16,8 +17,23 @@ export function Layout<IconType>({
|
|
|
16
17
|
const { user } = useAppStore(s => ({
|
|
17
18
|
user: s.user,
|
|
18
19
|
}));
|
|
19
|
-
|
|
20
|
+
const redirected = useRef(false);
|
|
21
|
+
|
|
22
|
+
// Yonlendirme render govdesinde degil effect'te yapiliyor: render sirasindaki
|
|
23
|
+
// yan etki React kuralini ihlal ediyor ve StrictMode'da iki kez tetikleniyordu.
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (user || redirected.current) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
redirected.current = true;
|
|
20
29
|
logout?.('redirect');
|
|
30
|
+
}, [user, logout]);
|
|
31
|
+
|
|
32
|
+
// Oturum yokken children RENDER EDILMEZ. Eskiden `logout('redirect')` cagrilip
|
|
33
|
+
// yine de tum layout donuluyordu; tarayici korumali ekrani boyayip ancak
|
|
34
|
+
// ondan sonra login'e gidiyordu (panelin bir an gorunup kaybolmasi).
|
|
35
|
+
if (!user) {
|
|
36
|
+
return <LoadingScreen id="layout-auth-redirect" />;
|
|
21
37
|
}
|
|
22
38
|
|
|
23
39
|
return (
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { MinLength } from 'class-validator';
|
|
2
|
+
import { Form } from '../form/Form';
|
|
3
|
+
import { Input } from '../form/Input';
|
|
4
|
+
import { LoginForm as LoginFormInterface, LoginResponse } from '../../types/Login';
|
|
5
|
+
import { setAuthToken } from '../../api/ApiConfig';
|
|
6
|
+
import { login } from '../../utils/login';
|
|
7
|
+
import { User } from '../../types/User';
|
|
8
|
+
import { authLogin } from '../..';
|
|
9
|
+
|
|
10
|
+
@Form<LoginFormInterface, LoginResponse<User>>({
|
|
11
|
+
onSubmit: authLogin,
|
|
12
|
+
onSubmitSuccess: (data: LoginResponse<User>) => {
|
|
13
|
+
setAuthToken(data.access_token);
|
|
14
|
+
login(data.admin as User, data.access_token, () => {
|
|
15
|
+
window.location.href = '/';
|
|
16
|
+
});
|
|
17
|
+
},
|
|
18
|
+
type: 'formData',
|
|
19
|
+
})
|
|
20
|
+
export class DefaultLoginForm {
|
|
21
|
+
@MinLength(3)
|
|
22
|
+
@Input({
|
|
23
|
+
label: 'Username',
|
|
24
|
+
})
|
|
25
|
+
username: string;
|
|
26
|
+
|
|
27
|
+
@Input({
|
|
28
|
+
label: 'Password',
|
|
29
|
+
inputType: 'password',
|
|
30
|
+
})
|
|
31
|
+
password: string;
|
|
32
|
+
}
|
|
@@ -8,7 +8,7 @@ const isFieldSensitive = (fieldName: string): boolean => {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
export type InputTypes = 'input' | 'textarea' | 'file-upload' | 'checkbox' | 'hidden' | 'nested';
|
|
11
|
-
export type ExtendedInputTypes = InputTypes | 'select';
|
|
11
|
+
export type ExtendedInputTypes = InputTypes | 'select' | 'custom';
|
|
12
12
|
|
|
13
13
|
export interface InputOptions {
|
|
14
14
|
type?: InputTypes;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { ExtendedInput, ExtendedInputOptions, InputConfiguration, InputOptions } from '../Input';
|
|
3
|
+
|
|
4
|
+
export interface CustomRenderProps {
|
|
5
|
+
fieldName: string;
|
|
6
|
+
value: unknown;
|
|
7
|
+
onChange: (value: unknown) => void;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface CustomInputOptions extends Omit<InputOptions, 'type'> {
|
|
12
|
+
render: (props: CustomRenderProps) => ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface CustomInputConfiguration extends InputConfiguration {
|
|
16
|
+
type: 'custom';
|
|
17
|
+
render: (props: CustomRenderProps) => ReactNode;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//NOTE: the render function travels through Reflect metadata like any other option value.
|
|
21
|
+
export function CustomInput(options: CustomInputOptions): PropertyDecorator {
|
|
22
|
+
return ExtendedInput({
|
|
23
|
+
...options,
|
|
24
|
+
type: 'custom',
|
|
25
|
+
} as ExtendedInputOptions);
|
|
26
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,11 @@ export { FormPage } from './components/form/FormPage';
|
|
|
21
21
|
export { Form, type OnSubmitFN } from './decorators/form/Form';
|
|
22
22
|
export { Input } from './decorators/form/Input';
|
|
23
23
|
export { SelectInput } from './decorators/form/inputs/SelectInput';
|
|
24
|
+
export {
|
|
25
|
+
CustomInput,
|
|
26
|
+
type CustomInputOptions,
|
|
27
|
+
type CustomRenderProps,
|
|
28
|
+
} from './decorators/form/inputs/CustomInput';
|
|
24
29
|
//for nested form fields
|
|
25
30
|
export { getInputFields } from './decorators/form/Input';
|
|
26
31
|
|
|
@@ -29,6 +34,7 @@ export { Panel } from './components/Panel';
|
|
|
29
34
|
|
|
30
35
|
//DASHBOARD
|
|
31
36
|
export { Counter } from './components/Counter';
|
|
37
|
+
export { Dashboard, DashboardGrid, DashboardItem } from './components/dashboard';
|
|
32
38
|
|
|
33
39
|
//LAYOUT
|
|
34
40
|
export { Layout } from './components/layout';
|
|
@@ -37,6 +43,31 @@ export { Layout } from './components/layout';
|
|
|
37
43
|
export { Login } from './components/Login';
|
|
38
44
|
export { login } from './utils/login';
|
|
39
45
|
export { logout } from './utils/logout';
|
|
46
|
+
export { DefaultLoginForm } from './decorators/auth/DefaultLoginForm';
|
|
47
|
+
|
|
48
|
+
//API
|
|
49
|
+
export {
|
|
50
|
+
initApi,
|
|
51
|
+
initAuthToken,
|
|
52
|
+
setAuthToken,
|
|
53
|
+
setAuthLogout,
|
|
54
|
+
getAxiosInstance,
|
|
55
|
+
axiosInstance,
|
|
56
|
+
} from './api/ApiConfig';
|
|
57
|
+
export {
|
|
58
|
+
getAll,
|
|
59
|
+
getOne,
|
|
60
|
+
create,
|
|
61
|
+
createFormData,
|
|
62
|
+
update,
|
|
63
|
+
updateFormData,
|
|
64
|
+
updateSimple,
|
|
65
|
+
remove,
|
|
66
|
+
} from './api/CrudApi';
|
|
67
|
+
export { login as authLogin } from './api/AuthApi';
|
|
68
|
+
|
|
69
|
+
//TYPES
|
|
70
|
+
export type { LoginForm, LoginResponse } from './types/Login';
|
|
40
71
|
|
|
41
72
|
//SERVICES
|
|
42
73
|
export { updateDetailsData } from './services/DataService';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// SCSS Color Variables - Dark Theme
|
|
2
|
+
// ⚠️ IMPORTANT: If you change any variables here, you MUST update COLOR_SYSTEM_GUIDE.md
|
|
3
|
+
$dark-void: #1a1a1a; // Darkest background
|
|
4
|
+
$dark-slate: #2b2b2b; // Dark background
|
|
5
|
+
$dark-mist: #333333; // Medium dark background
|
|
6
|
+
$dark-silver: #444444; // Lighter dark background
|
|
7
|
+
|
|
8
|
+
$dark-pearl: #ffffff; // White
|
|
9
|
+
$dark-ash: #e0e0e0; // Light gray
|
|
10
|
+
$dark-dust: #999999; // Medium gray
|
|
11
|
+
|
|
12
|
+
$dark-ocean: #007bff; // Primary blue
|
|
13
|
+
$dark-sky: #66b2ff; // Light blue (focus)
|
|
14
|
+
$dark-emerald: #4caf50; // Success green
|
|
15
|
+
$dark-ruby: #ff4d4f; // Error red
|
|
16
|
+
|
|
17
|
+
.layout {
|
|
18
|
+
// Backgrounds
|
|
19
|
+
--prp-bg-primary: #{$dark-void};
|
|
20
|
+
--prp-bg-secondary: #{$dark-slate};
|
|
21
|
+
--prp-bg-tertiary: #{$dark-mist};
|
|
22
|
+
--prp-bg-white: #{$dark-pearl};
|
|
23
|
+
--prp-bg-button-primary: #{$dark-ocean};
|
|
24
|
+
--prp-bg-button-success: #{$dark-emerald};
|
|
25
|
+
|
|
26
|
+
// Text
|
|
27
|
+
--prp-text-primary: #{$dark-pearl};
|
|
28
|
+
--prp-text-secondary: #{$dark-ash};
|
|
29
|
+
--prp-text-muted: #{$dark-dust};
|
|
30
|
+
|
|
31
|
+
// Borders
|
|
32
|
+
--prp-border-primary: #{$dark-silver};
|
|
33
|
+
--prp-border-light: #{$dark-ash};
|
|
34
|
+
|
|
35
|
+
// Colors
|
|
36
|
+
--prp-color-focus: #{$dark-sky};
|
|
37
|
+
--prp-color-success: #{$dark-emerald};
|
|
38
|
+
--prp-color-error: #{$dark-ruby};
|
|
39
|
+
|
|
40
|
+
// Shadows
|
|
41
|
+
--prp-shadow-light: rgba(0, 0, 0, 0.05);
|
|
42
|
+
--prp-shadow-medium: rgba(0, 0, 0, 0.2);
|
|
43
|
+
--prp-shadow-dark: rgba(0, 0, 0, 0.3);
|
|
44
|
+
--prp-shadow-focus: rgba(102, 178, 255, 0.2);
|
|
45
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
.panel-button {
|
|
2
2
|
padding: 0.5rem 1rem;
|
|
3
|
-
border: 1px solid
|
|
3
|
+
border: 1px solid var(--prp-border-primary);
|
|
4
4
|
border-radius: 4px;
|
|
5
|
-
background-color:
|
|
6
|
-
color:
|
|
5
|
+
background-color: var(--prp-bg-button-primary);
|
|
6
|
+
color: var(--prp-text-primary);
|
|
7
7
|
font-size: 0.875rem;
|
|
8
8
|
font-weight: 500;
|
|
9
9
|
cursor: pointer;
|
|
@@ -13,10 +13,10 @@ input.apple-switch {
|
|
|
13
13
|
outline: none;
|
|
14
14
|
width: 50px;
|
|
15
15
|
height: 30px;
|
|
16
|
-
background-color:
|
|
16
|
+
background-color: var(--prp-bg-white);
|
|
17
17
|
border: 1px solid #d9dadc;
|
|
18
18
|
border-radius: 50px;
|
|
19
|
-
box-shadow: inset -20px 0 0 0
|
|
19
|
+
box-shadow: inset -20px 0 0 0 var(--prp-bg-white);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
input.apple-switch:after {
|
|
@@ -28,15 +28,15 @@ input.apple-switch:after {
|
|
|
28
28
|
width: 26px;
|
|
29
29
|
height: 26px;
|
|
30
30
|
border-radius: 50%;
|
|
31
|
-
box-shadow: 2px 4px 6px
|
|
31
|
+
box-shadow: 2px 4px 6px var(--prp-shadow-medium);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
input.apple-switch:checked {
|
|
35
|
-
box-shadow: inset 20px 0 0 0
|
|
36
|
-
border-color:
|
|
35
|
+
box-shadow: inset 20px 0 0 0 var(--prp-color-success);
|
|
36
|
+
border-color: var(--prp-color-success);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
input.apple-switch:checked:after {
|
|
40
40
|
left: 20px;
|
|
41
|
-
box-shadow: -2px 4px 3px
|
|
41
|
+
box-shadow: -2px 4px 3px var(--prp-shadow-light);
|
|
42
42
|
}
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
align-items: center;
|
|
5
5
|
padding: 1rem;
|
|
6
6
|
margin-bottom: 1.5rem;
|
|
7
|
-
background-color:
|
|
7
|
+
background-color: var(--prp-bg-tertiary);
|
|
8
8
|
border-radius: 8px 0 0 8px;
|
|
9
9
|
|
|
10
10
|
.form-title {
|
|
11
11
|
margin: 0;
|
|
12
12
|
font-size: 1.5rem;
|
|
13
13
|
font-weight: 600;
|
|
14
|
-
color:
|
|
14
|
+
color: var(--prp-text-secondary);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
.form-header-inner {
|
|
@@ -23,50 +23,52 @@
|
|
|
23
23
|
.export-button,
|
|
24
24
|
.import-button {
|
|
25
25
|
padding: 0.5rem 1rem;
|
|
26
|
-
border: 1px solid
|
|
26
|
+
border: 1px solid var(--prp-border-primary);
|
|
27
27
|
border-radius: 4px;
|
|
28
|
-
background-color:
|
|
29
|
-
color:
|
|
28
|
+
background-color: var(--prp-bg-secondary);
|
|
29
|
+
color: var(--prp-text-secondary);
|
|
30
30
|
font-size: 0.875rem;
|
|
31
31
|
cursor: pointer;
|
|
32
32
|
transition: all 0.2s ease;
|
|
33
33
|
|
|
34
34
|
&:hover {
|
|
35
|
-
background-color:
|
|
36
|
-
border-color:
|
|
35
|
+
background-color: var(--prp-bg-tertiary);
|
|
36
|
+
border-color: var(--prp-border-primary);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
&.dark-theme {
|
|
42
|
-
background-color:
|
|
42
|
+
background-color: var(--prp-bg-primary);
|
|
43
43
|
box-shadow: none;
|
|
44
44
|
|
|
45
45
|
.form-title {
|
|
46
|
-
color:
|
|
46
|
+
color: var(--prp-text-primary);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
.form-actions {
|
|
50
50
|
.export-button {
|
|
51
|
-
background-color:
|
|
52
|
-
border-color:
|
|
53
|
-
color:
|
|
51
|
+
background-color: var(--prp-bg-primary);
|
|
52
|
+
border-color: var(--prp-bg-primary);
|
|
53
|
+
color: var(--prp-text-primary);
|
|
54
54
|
|
|
55
55
|
&:hover {
|
|
56
|
-
background-color:
|
|
57
|
-
border-color:
|
|
56
|
+
background-color: var(--prp-bg-primary);
|
|
57
|
+
border-color: var(--prp-bg-primary);
|
|
58
|
+
opacity: 0.9;
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
.import-button {
|
|
62
63
|
.import-label {
|
|
63
|
-
background-color:
|
|
64
|
-
border-color:
|
|
65
|
-
color:
|
|
64
|
+
background-color: var(--prp-bg-primary);
|
|
65
|
+
border-color: var(--prp-bg-primary);
|
|
66
|
+
color: var(--prp-text-primary);
|
|
66
67
|
|
|
67
68
|
&:hover {
|
|
68
|
-
background-color:
|
|
69
|
-
border-color:
|
|
69
|
+
background-color: var(--prp-bg-primary);
|
|
70
|
+
border-color: var(--prp-bg-primary);
|
|
71
|
+
opacity: 0.9;
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
}
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
.uploader {
|
|
2
2
|
&-container {
|
|
3
3
|
padding: 1.5rem;
|
|
4
|
-
border: 2px dashed var(--border-
|
|
4
|
+
border: 2px dashed var(--prp-border-light);
|
|
5
5
|
border-radius: 8px;
|
|
6
|
-
background-color: var(--bg-
|
|
6
|
+
background-color: var(--prp-bg-white);
|
|
7
7
|
transition: all 0.3s ease;
|
|
8
8
|
|
|
9
9
|
&:hover {
|
|
10
|
-
border-color: var(--primary
|
|
11
|
-
background-color: var(--bg-
|
|
10
|
+
border-color: var(--prp-bg-button-primary);
|
|
11
|
+
background-color: var(--prp-bg-tertiary);
|
|
12
|
+
opacity: 0.9;
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
&-button {
|
|
16
|
-
background-color: var(--primary
|
|
17
|
+
background-color: var(--prp-bg-button-primary);
|
|
17
18
|
color: var(--button-text-color, white);
|
|
18
19
|
padding: 0.75rem 1.5rem;
|
|
19
20
|
border: none;
|
|
@@ -23,11 +24,13 @@
|
|
|
23
24
|
transition: background-color 0.3s ease;
|
|
24
25
|
|
|
25
26
|
&:hover {
|
|
26
|
-
background-color: var(--
|
|
27
|
+
background-color: var(--prp-bg-button-primary);
|
|
28
|
+
opacity: 0.9;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
&:active {
|
|
30
|
-
background-color: var(--
|
|
32
|
+
background-color: var(--prp-bg-button-primary);
|
|
33
|
+
opacity: 0.8;
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
36
|
|
|
@@ -38,49 +41,24 @@
|
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
&-file {
|
|
41
|
-
background-color: var(--
|
|
44
|
+
background-color: var(--prp-bg-secondary);
|
|
42
45
|
padding: 1rem;
|
|
43
46
|
border-radius: 6px;
|
|
44
|
-
box-shadow: 0 2px 4px var(--shadow-
|
|
47
|
+
box-shadow: 0 2px 4px var(--prp-shadow-light);
|
|
45
48
|
display: grid;
|
|
46
49
|
gap: 0.5rem;
|
|
47
50
|
|
|
48
51
|
p {
|
|
49
52
|
margin: 0;
|
|
50
|
-
color: var(--text-
|
|
53
|
+
color: var(--prp-text-muted);
|
|
51
54
|
font-size: 0.875rem;
|
|
52
55
|
|
|
53
56
|
&:first-child {
|
|
54
|
-
color: var(--primary
|
|
57
|
+
color: var(--prp-bg-button-primary);
|
|
55
58
|
font-weight: 500;
|
|
56
59
|
}
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
:
|
|
62
|
-
--border-color: #424242;
|
|
63
|
-
--bg-color: #1e1e1e;
|
|
64
|
-
--bg-hover-color: #2d2d2d;
|
|
65
|
-
--primary-color: #64b5f6;
|
|
66
|
-
--primary-hover-color: #42a5f5;
|
|
67
|
-
--primary-active-color: #2196f3;
|
|
68
|
-
--button-text-color: #ffffff;
|
|
69
|
-
--file-bg-color: #2d2d2d;
|
|
70
|
-
--shadow-color: rgba(0, 0, 0, 0.2);
|
|
71
|
-
--text-color: #e0e0e0;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Dark theme
|
|
75
|
-
[data-theme='dark'] {
|
|
76
|
-
--border-color: #424242;
|
|
77
|
-
--bg-color: #1e1e1e;
|
|
78
|
-
--bg-hover-color: #2d2d2d;
|
|
79
|
-
--primary-color: #64b5f6;
|
|
80
|
-
--primary-hover-color: #42a5f5;
|
|
81
|
-
--primary-active-color: #2196f3;
|
|
82
|
-
--button-text-color: #ffffff;
|
|
83
|
-
--file-bg-color: #2d2d2d;
|
|
84
|
-
--shadow-color: rgba(0, 0, 0, 0.2);
|
|
85
|
-
--text-color: #e0e0e0;
|
|
86
|
-
}
|
|
64
|
+
// Note: Background colors are now managed in base/_variables.scss
|
package/src/styles/counter.scss
CHANGED
|
@@ -1,42 +1,34 @@
|
|
|
1
|
-
// Variables
|
|
2
|
-
$primary-color: #007bff;
|
|
3
|
-
$text-color: #ffffff;
|
|
4
|
-
$bg-color: #1a1a1a;
|
|
5
|
-
$shadow-color: rgba(0, 0, 0, 0.3);
|
|
6
|
-
|
|
7
1
|
// Mixins
|
|
8
2
|
@mixin flex-center {
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
display: flex;
|
|
4
|
+
align-items: center;
|
|
11
5
|
}
|
|
12
6
|
|
|
13
7
|
.counter {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
margin: 20px auto;
|
|
23
|
-
}
|
|
8
|
+
&-container {
|
|
9
|
+
@include flex-center;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
padding: 20px;
|
|
12
|
+
border-radius: 8px;
|
|
13
|
+
max-width: 300px;
|
|
14
|
+
margin: 20px auto;
|
|
15
|
+
}
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
&-image {
|
|
18
|
+
margin-bottom: 15px;
|
|
19
|
+
}
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
21
|
+
&-text {
|
|
22
|
+
font-size: 18px;
|
|
23
|
+
margin-bottom: 15px;
|
|
24
|
+
color: var(--prp-text-primary);
|
|
25
|
+
}
|
|
34
26
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
27
|
+
&-value {
|
|
28
|
+
font-size: 24px;
|
|
29
|
+
font-weight: bold;
|
|
30
|
+
min-width: 40px;
|
|
31
|
+
text-align: center;
|
|
32
|
+
color: var(--prp-text-primary);
|
|
33
|
+
}
|
|
42
34
|
}
|
package/src/styles/details.scss
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
:root {
|
|
2
|
-
--background-color: #1a1a1a;
|
|
3
|
-
--text-color: #e0e0e0;
|
|
4
|
-
--label-color: #a0a0a0;
|
|
5
|
-
--empty-value-color: #666;
|
|
6
|
-
--error-color: #ff6b6b;
|
|
7
|
-
--shadow-color: rgba(0, 0, 0, 0.3);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
1
|
.details-page {
|
|
11
2
|
padding: 2rem;
|
|
12
3
|
max-width: 1200px;
|
|
@@ -17,11 +8,11 @@
|
|
|
17
8
|
}
|
|
18
9
|
|
|
19
10
|
.details-item {
|
|
20
|
-
background: var(--
|
|
11
|
+
background: var(--prp-bg-primary);
|
|
21
12
|
border-radius: 8px;
|
|
22
13
|
padding: 1.5rem;
|
|
23
14
|
margin-bottom: 1rem;
|
|
24
|
-
box-shadow: 0 2px 4px var(--shadow-
|
|
15
|
+
box-shadow: 0 2px 4px var(--prp-shadow-dark);
|
|
25
16
|
transition: transform 0.2s ease;
|
|
26
17
|
|
|
27
18
|
&:hover {
|
|
@@ -30,7 +21,7 @@
|
|
|
30
21
|
|
|
31
22
|
.item-label {
|
|
32
23
|
font-size: 0.875rem;
|
|
33
|
-
color: var(--
|
|
24
|
+
color: var(--prp-text-secondary);
|
|
34
25
|
text-transform: uppercase;
|
|
35
26
|
letter-spacing: 0.5px;
|
|
36
27
|
margin-bottom: 0.5rem;
|
|
@@ -38,14 +29,14 @@
|
|
|
38
29
|
|
|
39
30
|
.item-value {
|
|
40
31
|
font-size: 1rem;
|
|
41
|
-
color: var(--text-
|
|
32
|
+
color: var(--prp-text-muted);
|
|
42
33
|
line-height: 1.5;
|
|
43
34
|
word-break: break-word;
|
|
44
35
|
|
|
45
36
|
&:empty {
|
|
46
37
|
&::after {
|
|
47
38
|
content: '—';
|
|
48
|
-
color: var(--
|
|
39
|
+
color: var(--prp-text-muted);
|
|
49
40
|
}
|
|
50
41
|
}
|
|
51
42
|
}
|
|
@@ -62,5 +53,5 @@
|
|
|
62
53
|
.error-container {
|
|
63
54
|
padding: 2rem;
|
|
64
55
|
text-align: center;
|
|
65
|
-
color: var(--
|
|
56
|
+
color: var(--prp-color-error);
|
|
66
57
|
}
|