mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Merge branch 'main' of https://github.com/IRBorisov/ConceptPortal
# Conflicts: # rsconcept/frontend/src/index.css
This commit is contained in:
commit
d8002923cb
|
@ -20,7 +20,7 @@ This readme file is used mostly to document project dependencies
|
||||||
- react-tabs
|
- react-tabs
|
||||||
- react-intl
|
- react-intl
|
||||||
- react-data-table-component
|
- react-data-table-component
|
||||||
- react-dropdown-select
|
- react-select
|
||||||
- react-error-boundary
|
- react-error-boundary
|
||||||
- reagraph
|
- reagraph
|
||||||
- react-tooltip
|
- react-tooltip
|
||||||
|
|
3
TODO.txt
3
TODO.txt
|
@ -9,12 +9,11 @@ For more specific TODOs see comments in code
|
||||||
- блок организации библиотеки моделей
|
- блок организации библиотеки моделей
|
||||||
- проектный модуль?
|
- проектный модуль?
|
||||||
- обратная связь - система баг репортов
|
- обратная связь - система баг репортов
|
||||||
|
- система обработки ошибок backend
|
||||||
|
|
||||||
[Tech]
|
[Tech]
|
||||||
- create custom Select component
|
|
||||||
- reload react-data-table-component
|
- reload react-data-table-component
|
||||||
|
|
||||||
[deployment]
|
[deployment]
|
||||||
- database backup daemon
|
|
||||||
- logs collection
|
- logs collection
|
||||||
- status dashboard for servers
|
- status dashboard for servers
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# ======== Multi-stage base ==========
|
# ======== Multi-stage base ==========
|
||||||
FROM node:bullseye-slim as node-base
|
FROM node:18-bullseye-slim as node-base
|
||||||
RUN apt-get update -qq && \
|
RUN apt-get update -qq && \
|
||||||
apt-get upgrade -y && \
|
apt-get upgrade -y && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
1782
rsconcept/frontend/package-lock.json
generated
1782
rsconcept/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -12,44 +12,44 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/lr": "^1.3.9",
|
"@lezer/lr": "^1.3.10",
|
||||||
"@uiw/codemirror-themes": "^4.21.9",
|
"@uiw/codemirror-themes": "^4.21.13",
|
||||||
"@uiw/react-codemirror": "^4.21.9",
|
"@uiw/react-codemirror": "^4.21.13",
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.5.0",
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-data-table-component": "^7.5.3",
|
"react-data-table-component": "^7.5.4",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-dropdown-select": "^4.10.0",
|
"react-error-boundary": "^4.0.11",
|
||||||
"react-error-boundary": "^4.0.10",
|
|
||||||
"react-intl": "^6.4.4",
|
"react-intl": "^6.4.4",
|
||||||
"react-loader-spinner": "^5.3.4",
|
"react-loader-spinner": "^5.4.5",
|
||||||
"react-router-dom": "^6.14.2",
|
"react-router-dom": "^6.15.0",
|
||||||
|
"react-select": "^5.7.4",
|
||||||
"react-tabs": "^6.0.2",
|
"react-tabs": "^6.0.2",
|
||||||
"react-toastify": "^9.1.3",
|
"react-toastify": "^9.1.3",
|
||||||
"react-tooltip": "^5.19.0",
|
"react-tooltip": "^5.21.3",
|
||||||
"reagraph": "^4.11.1"
|
"reagraph": "^4.13.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lezer/generator": "^1.4.0",
|
"@lezer/generator": "^1.5.0",
|
||||||
"@types/jest": "^29.5.3",
|
"@types/jest": "^29.5.4",
|
||||||
"@types/node": "^20.4.5",
|
"@types/node": "^20.5.9",
|
||||||
"@types/react": "^18.2.15",
|
"@types/react": "^18.2.21",
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.2.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
"@typescript-eslint/eslint-plugin": "^6.6.0",
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
"@typescript-eslint/parser": "^6.6.0",
|
||||||
"@vitejs/plugin-react": "^4.0.3",
|
"@vitejs/plugin-react": "^4.0.4",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.15",
|
||||||
"eslint": "^8.45.0",
|
"eslint": "^8.48.0",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.3",
|
"eslint-plugin-react-refresh": "^0.4.3",
|
||||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||||
"jest": "^29.6.2",
|
"jest": "^29.6.4",
|
||||||
"postcss": "^8.4.27",
|
"postcss": "^8.4.29",
|
||||||
"tailwindcss": "^3.3.3",
|
"tailwindcss": "^3.3.3",
|
||||||
"ts-jest": "^29.1.1",
|
"ts-jest": "^29.1.1",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^4.4.5"
|
"vite": "^4.4.9"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"preset": "ts-jest",
|
"preset": "ts-jest",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { useRef } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { CheckboxChecked } from '../Icons';
|
||||||
import Label from './Label';
|
import Label from './Label';
|
||||||
|
|
||||||
export interface CheckboxProps {
|
export interface CheckboxProps {
|
||||||
|
@ -9,51 +10,52 @@ export interface CheckboxProps {
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
widthClass?: string
|
widthClass?: string
|
||||||
tooltip?: string
|
tooltip?: string
|
||||||
value?: boolean
|
|
||||||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
value: boolean
|
||||||
|
setValue?: (newValue: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-fit', value, onChange }: CheckboxProps) {
|
function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-fit', value, setValue }: CheckboxProps) {
|
||||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
const cursor = useMemo(
|
||||||
|
() => {
|
||||||
const cursor = disabled ? 'cursor-not-allowed' : 'cursor-pointer';
|
if (disabled) {
|
||||||
|
return 'cursor-not-allowed';
|
||||||
|
} else if (setValue) {
|
||||||
|
return 'cursor-pointer';
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}, [disabled, setValue]);
|
||||||
|
const bgColor = useMemo(
|
||||||
|
() => {
|
||||||
|
return value !== false ? 'clr-primary' : 'clr-app'
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
function handleClick(event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void {
|
function handleClick(event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!disabled) {
|
if (disabled || !setValue) {
|
||||||
inputRef.current?.click();
|
return;
|
||||||
}
|
}
|
||||||
|
setValue(!value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={'flex [&:not(:first-child)]:mt-3 clr-outline focus:outline-dotted focus:outline-1 ' + widthClass}
|
id={id}
|
||||||
|
className={`flex items-center [&:not(:first-child)]:mt-3 clr-outline focus:outline-dotted focus:outline-1 ${widthClass}`}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<input id={id} type='checkbox' ref={inputRef}
|
<div className={`relative peer w-4 h-4 shrink-0 mt-0.5 border rounded-sm appearance-none ${bgColor} ${cursor}`} />
|
||||||
className={`relative peer w-4 h-4 shrink-0 mt-0.5 border rounded-sm appearance-none clr-checkbox ${cursor}`}
|
|
||||||
required={required}
|
|
||||||
disabled={disabled}
|
|
||||||
checked={value}
|
|
||||||
onChange={onChange}
|
|
||||||
tabIndex={-1}
|
|
||||||
/>
|
|
||||||
{ label &&
|
{ label &&
|
||||||
<Label
|
<Label
|
||||||
className={`${cursor} px-2`}
|
className={`${cursor} px-2 text-start`}
|
||||||
text={label}
|
text={label}
|
||||||
required={required}
|
required={required}
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
/>}
|
/>}
|
||||||
<svg
|
{value && <CheckboxChecked />}
|
||||||
className='absolute hidden w-3 h-3 mt-1 ml-0.5 text-white pointer-events-none peer-checked:block'
|
|
||||||
viewBox='0 0 512 512'
|
|
||||||
fill='currentColor'
|
|
||||||
>
|
|
||||||
<path d='M470.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 338.7l233.4-233.3c12.5-12.5 32.8-12.5 45.3 0z' />
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
import { PropsWithRef } from 'react';
|
|
||||||
import Select, { SelectProps } from 'react-dropdown-select';
|
|
||||||
|
|
||||||
interface ConceptSelectProps<T>
|
|
||||||
extends Omit<PropsWithRef<SelectProps<T>>, 'noDataLabel'> {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function ConceptSelect<T extends object | string>({ className, ...props }: ConceptSelectProps<T>) {
|
|
||||||
return (
|
|
||||||
<Select
|
|
||||||
className={`overflow-x-ellipsis whitespace-nowrap clr-border clr-input outline-none ${className}`}
|
|
||||||
{...props}
|
|
||||||
noDataLabel='Список пуст'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ConceptSelect;
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import Select, { GroupBase, Props, StylesConfig } from 'react-select';
|
||||||
|
|
||||||
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
|
import { selectDarkT, selectLightT } from '../../utils/color';
|
||||||
|
|
||||||
|
interface ConceptSelectSingleProps<
|
||||||
|
Option,
|
||||||
|
Group extends GroupBase<Option> = GroupBase<Option>
|
||||||
|
>
|
||||||
|
extends Omit<Props<Option, false, Group>, 'theme'> {
|
||||||
|
}
|
||||||
|
|
||||||
|
function ConceptSelectSingle<
|
||||||
|
Option,
|
||||||
|
Group extends GroupBase<Option> = GroupBase<Option>
|
||||||
|
> ({ ...props }: ConceptSelectSingleProps<Option, Group>) {
|
||||||
|
const { darkMode, colors } = useConceptTheme();
|
||||||
|
const themeColors = useMemo(
|
||||||
|
() => {
|
||||||
|
return !darkMode ? selectLightT : selectDarkT;
|
||||||
|
}, [darkMode]);
|
||||||
|
|
||||||
|
const adjustedStyles: StylesConfig<Option, false, Group> = useMemo(
|
||||||
|
() => {
|
||||||
|
return {
|
||||||
|
control: (styles, { isDisabled }) => {
|
||||||
|
return {
|
||||||
|
...styles,
|
||||||
|
borderRadius: '0.25rem',
|
||||||
|
cursor: isDisabled ? 'not-allowed' : 'pointer'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
option: (styles, { isSelected }) => {
|
||||||
|
return {
|
||||||
|
...styles,
|
||||||
|
backgroundColor: isSelected ? colors.bgSelected : styles.backgroundColor,
|
||||||
|
color: isSelected ? colors.fgSelected : styles.color,
|
||||||
|
borderWidth: '1px',
|
||||||
|
borderColor: colors.border
|
||||||
|
};
|
||||||
|
},
|
||||||
|
input: (styles) => ({...styles}),
|
||||||
|
placeholder: (styles) => ({...styles}),
|
||||||
|
singleValue: (styles) => ({...styles}),
|
||||||
|
};
|
||||||
|
}, [colors]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
noOptionsMessage={() => 'Список пуст'}
|
||||||
|
theme={theme => ({
|
||||||
|
...theme,
|
||||||
|
borderRadius: 0,
|
||||||
|
colors: {
|
||||||
|
...theme.colors,
|
||||||
|
...themeColors
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
styles={adjustedStyles}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConceptSelectSingle;
|
|
@ -1,15 +1,15 @@
|
||||||
import Checkbox from './Checkbox';
|
import Checkbox from './Checkbox';
|
||||||
|
|
||||||
interface DropdownCheckboxProps {
|
interface DropdownCheckboxProps {
|
||||||
|
value: boolean
|
||||||
label?: string
|
label?: string
|
||||||
tooltip?: string
|
tooltip?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
value?: boolean
|
setValue?: (newValue: boolean) => void
|
||||||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownCheckbox({ tooltip, onChange, disabled, ...props }: DropdownCheckboxProps) {
|
function DropdownCheckbox({ tooltip, setValue, disabled, ...props }: DropdownCheckboxProps) {
|
||||||
const behavior = (onChange && !disabled ? 'clr-hover' : '');
|
const behavior = (setValue && !disabled ? 'clr-hover' : '');
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
|
@ -18,7 +18,7 @@ function DropdownCheckbox({ tooltip, onChange, disabled, ...props }: DropdownChe
|
||||||
<Checkbox
|
<Checkbox
|
||||||
widthClass='w-full'
|
widthClass='w-full'
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={onChange}
|
setValue={setValue}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,18 +4,22 @@ import { UploadIcon } from '../Icons';
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
import Label from './Label';
|
import Label from './Label';
|
||||||
|
|
||||||
interface FileInputProps {
|
interface FileInputProps
|
||||||
id?: string
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'className' | 'title' | 'style' | 'accept' | 'type'> {
|
||||||
required?: boolean
|
|
||||||
label: string
|
label: string
|
||||||
|
tooltip?: string
|
||||||
acceptType?: string
|
acceptType?: string
|
||||||
widthClass?: string
|
widthClass?: string
|
||||||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function FileInput({ id, required, label, acceptType, widthClass = 'w-full', onChange }: FileInputProps) {
|
function FileInput({
|
||||||
|
label, acceptType, tooltip,
|
||||||
|
widthClass = 'w-fit', onChange,
|
||||||
|
...props
|
||||||
|
}: FileInputProps) {
|
||||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||||
const [labelText, setLabelText] = useState('');
|
const [fileName, setFileName] = useState('');
|
||||||
|
|
||||||
const handleUploadClick = () => {
|
const handleUploadClick = () => {
|
||||||
inputRef.current?.click();
|
inputRef.current?.click();
|
||||||
|
@ -23,9 +27,9 @@ function FileInput({ id, required, label, acceptType, widthClass = 'w-full', onC
|
||||||
|
|
||||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (event.target.files && event.target.files.length > 0) {
|
if (event.target.files && event.target.files.length > 0) {
|
||||||
setLabelText(event.target.files[0].name)
|
setFileName(event.target.files[0].name)
|
||||||
} else {
|
} else {
|
||||||
setLabelText('')
|
setFileName('')
|
||||||
}
|
}
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
onChange(event);
|
onChange(event);
|
||||||
|
@ -33,21 +37,22 @@ function FileInput({ id, required, label, acceptType, widthClass = 'w-full', onC
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'flex flex-col gap-2 py-2 [&:not(:first-child)]:mt-3 items-start ' + widthClass}>
|
<div className={`flex flex-col gap-2 py-2 mt-3 items-start ${widthClass}`}>
|
||||||
<input id={id} type='file'
|
<input type='file'
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
required={required}
|
|
||||||
style={{ display: 'none' }}
|
style={{ display: 'none' }}
|
||||||
accept={acceptType}
|
accept={acceptType}
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
|
{...props}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
text={label}
|
text={label}
|
||||||
icon={<UploadIcon/>}
|
icon={<UploadIcon/>}
|
||||||
onClick={handleUploadClick}
|
onClick={handleUploadClick}
|
||||||
|
tooltip={tooltip}
|
||||||
/>
|
/>
|
||||||
<Label
|
<Label
|
||||||
text={labelText}
|
text={fileName}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,7 +14,7 @@ function SubmitButton({
|
||||||
return (
|
return (
|
||||||
<button type='submit'
|
<button type='submit'
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
className={`px-4 py-2 inline-flex items-center gap-2 align-middle justify-center font-bold select-none disabled:cursor-not-allowed border rounded clr-btn-primary ${widthClass} ${loading ? ' cursor-progress' : ''}`}
|
className={`px-4 py-2 inline-flex items-center gap-2 align-middle justify-center font-semibold select-none disabled:cursor-not-allowed border rounded clr-btn-primary ${widthClass} ${loading ? ' cursor-progress' : ''}`}
|
||||||
disabled={disabled ?? loading}
|
disabled={disabled ?? loading}
|
||||||
>
|
>
|
||||||
{icon && <span>{icon}</span>}
|
{icon && <span>{icon}</span>}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { type InputHTMLAttributes } from 'react';
|
|
||||||
|
|
||||||
import Label from './Label';
|
import Label from './Label';
|
||||||
|
|
||||||
interface TextInputProps
|
interface TextInputProps
|
||||||
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className' | 'title'> {
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'className' | 'title'> {
|
||||||
id: string
|
id: string
|
||||||
label: string
|
label: string
|
||||||
tooltip?: string
|
tooltip?: string
|
||||||
|
|
64
rsconcept/frontend/src/components/Common/Tristate.tsx
Normal file
64
rsconcept/frontend/src/components/Common/Tristate.tsx
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { CheckboxChecked, CheckboxNull } from '../Icons';
|
||||||
|
import { CheckboxProps } from './Checkbox';
|
||||||
|
import Label from './Label';
|
||||||
|
|
||||||
|
export interface TristateProps
|
||||||
|
extends Omit<CheckboxProps, 'value' | 'setValue'> {
|
||||||
|
value: boolean | null
|
||||||
|
setValue?: (newValue: boolean | null) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-fit', value, setValue }: TristateProps) {
|
||||||
|
const cursor = useMemo(
|
||||||
|
() => {
|
||||||
|
if (disabled) {
|
||||||
|
return 'cursor-not-allowed';
|
||||||
|
} else if (setValue) {
|
||||||
|
return 'cursor-pointer';
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}, [disabled, setValue]);
|
||||||
|
const bgColor = useMemo(
|
||||||
|
() => {
|
||||||
|
return value !== false ? 'clr-primary' : 'clr-app'
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
function handleClick(event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void {
|
||||||
|
event.preventDefault();
|
||||||
|
if (disabled || !setValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value === false) {
|
||||||
|
setValue(null);
|
||||||
|
} else {
|
||||||
|
setValue(!value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
id={id}
|
||||||
|
className={`flex items-center [&:not(:first-child)]:mt-3 clr-outline focus:outline-dotted focus:outline-1 ${widthClass}`}
|
||||||
|
title={tooltip}
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<div className={`relative peer w-4 h-4 shrink-0 mt-0.5 border rounded-sm appearance-none ${bgColor} ${cursor}`} />
|
||||||
|
{ label &&
|
||||||
|
<Label
|
||||||
|
className={`${cursor} px-2 text-start`}
|
||||||
|
text={label}
|
||||||
|
required={required}
|
||||||
|
htmlFor={id}
|
||||||
|
/>}
|
||||||
|
{value && <CheckboxChecked />}
|
||||||
|
{value === null && <CheckboxNull />}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Checkbox;
|
|
@ -317,3 +317,27 @@ export function InDoor(props: IconProps) {
|
||||||
</IconSVG>
|
</IconSVG>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function CheckboxChecked() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className='absolute w-3 h-3 mt-1 ml-0.5'
|
||||||
|
viewBox='0 0 512 512'
|
||||||
|
fill='#ffffff'
|
||||||
|
>
|
||||||
|
<path d='M470.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 338.7l233.4-233.3c12.5-12.5 32.8-12.5 45.3 0z' />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CheckboxNull() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className='absolute w-3 h-3 mt-1 ml-0.5'
|
||||||
|
viewBox='0 0 512 512'
|
||||||
|
fill='#ffffff'
|
||||||
|
>
|
||||||
|
<path d='M2 7.75A.75.75 0 012.75 7h10a.75.75 0 010 1.5h-10A.75.75 0 012 7.75z' />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
import { Extension } from '@codemirror/state';
|
import { Extension } from '@codemirror/state';
|
||||||
import { tags as t } from '@lezer/highlight';
|
import { tags } from '@lezer/highlight';
|
||||||
import { createTheme } from '@uiw/codemirror-themes';
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||||
import { EditorView } from 'codemirror';
|
import { EditorView } from 'codemirror';
|
||||||
|
@ -64,9 +64,9 @@ function RSInput({
|
||||||
}, [internalRef, innerref]);
|
}, [internalRef, innerref]);
|
||||||
|
|
||||||
const cursor = useMemo(() => editable ? 'cursor-text': 'cursor-default', [editable]);
|
const cursor = useMemo(() => editable ? 'cursor-text': 'cursor-default', [editable]);
|
||||||
const lightTheme: Extension = useMemo(
|
const customTheme: Extension = useMemo(
|
||||||
() => createTheme({
|
() => createTheme({
|
||||||
theme: 'light',
|
theme: darkMode ? 'dark' : 'light',
|
||||||
settings: {
|
settings: {
|
||||||
fontFamily: 'inherit',
|
fontFamily: 'inherit',
|
||||||
background: editable ? colors.bgInput : colors.bgDefault,
|
background: editable ? colors.bgInput : colors.bgDefault,
|
||||||
|
@ -74,35 +74,15 @@ function RSInput({
|
||||||
selection: colors.bgHover
|
selection: colors.bgHover
|
||||||
},
|
},
|
||||||
styles: [
|
styles: [
|
||||||
{ tag: t.name, class: 'text-[#b266ff] cursor-default' }, // GlobalID
|
{ tag: tags.name, color: colors.fgPurple, cursor: 'default' }, // GlobalID
|
||||||
{ tag: t.variableName, class: 'text-[#24821a]' }, // LocalID
|
{ tag: tags.variableName, color: colors.fgGreen }, // LocalID
|
||||||
{ tag: t.propertyName, class: '' }, // Radical
|
{ tag: tags.propertyName, color: colors.fgTeal }, // Radical
|
||||||
{ tag: t.keyword, class: 'text-[#001aff]' }, // keywords
|
{ tag: tags.keyword, color: colors.fgBlue }, // keywords
|
||||||
{ tag: t.literal, class: 'text-[#001aff]' }, // literals
|
{ tag: tags.literal, color: colors.fgBlue }, // literals
|
||||||
{ tag: t.controlKeyword, class: 'font-semibold'}, // R | I | D
|
{ tag: tags.controlKeyword, fontWeight: '500'}, // R | I | D
|
||||||
{ tag: t.unit, class: 'text-[0.75rem]' }, // indicies
|
{ tag: tags.unit, fontSize: '0.75rem' }, // indicies
|
||||||
]
|
]
|
||||||
}), [editable, colors]);
|
}), [editable, colors, darkMode]);
|
||||||
|
|
||||||
const darkTheme: Extension = useMemo(
|
|
||||||
() => createTheme({
|
|
||||||
theme: 'dark',
|
|
||||||
settings: {
|
|
||||||
fontFamily: 'inherit',
|
|
||||||
background: editable ? colors.bgInput : colors.bgDefault,
|
|
||||||
foreground: colors.fgDefault,
|
|
||||||
selection: colors.bgHover
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: t.name, class: 'text-[#dfbfff] cursor-default' }, // GlobalID
|
|
||||||
{ tag: t.variableName, class: 'text-[#69bf60]' }, // LocalID
|
|
||||||
{ tag: t.propertyName, class: '' }, // Radical
|
|
||||||
{ tag: t.keyword, class: 'text-[#808dff]' }, // keywords
|
|
||||||
{ tag: t.literal, class: 'text-[#808dff]' }, // literals
|
|
||||||
{ tag: t.controlKeyword, class: 'font-semibold'}, // R | I | D
|
|
||||||
{ tag: t.unit, class: 'text-[0.75rem]' }, // indicies
|
|
||||||
]
|
|
||||||
}), [editable, colors]);
|
|
||||||
|
|
||||||
const editorExtensions = useMemo(
|
const editorExtensions = useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
@ -148,7 +128,7 @@ function RSInput({
|
||||||
<CodeMirror id={id}
|
<CodeMirror id={id}
|
||||||
ref={thisRef}
|
ref={thisRef}
|
||||||
basicSetup={editorSetup}
|
basicSetup={editorSetup}
|
||||||
theme={darkMode ? darkTheme : lightTheme}
|
theme={customTheme}
|
||||||
extensions={editorExtensions}
|
extensions={editorExtensions}
|
||||||
indentWithTab={false}
|
indentWithTab={false}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
|
|
@ -6,60 +6,48 @@
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/* Light Theme */
|
/* Light Theme */
|
||||||
--cl-bg-120: #ffffff;
|
--cl-bg-120: hsl(000, 000%, 100%);
|
||||||
--cl-bg-100: #f9fafb;
|
--cl-bg-100: hsl(220, 020%, 098%);
|
||||||
--cl-bg-80: #f3f4f6;
|
--cl-bg-80: hsl(220, 014%, 096%);
|
||||||
--cl-bg-60: #e5e7eb;
|
--cl-bg-60: hsl(220, 013%, 091%);
|
||||||
--cl-bg-40: #d1d5db;
|
--cl-bg-40: hsl(216, 012%, 084%);
|
||||||
|
|
||||||
--cl-fg-40: #8c8c8c;
|
--cl-fg-60: hsl(000, 000%, 055%);
|
||||||
--cl-fg-60: #777777;
|
--cl-fg-80: hsl(000, 000%, 047%);
|
||||||
--cl-fg-80: #333333;
|
--cl-fg-100: hsl(000, 000%, 000%);
|
||||||
--cl-fg-100: #000000;
|
|
||||||
|
|
||||||
--cl-prim-bg-100: #3377ff;
|
--cl-prim-bg-100: hsl(220, 100%, 060%);
|
||||||
--cl-prim-bg-80: #ccddff;
|
--cl-prim-bg-80: hsl(220, 100%, 090%);
|
||||||
--cl-prim-bg-60: #e0ebff;
|
--cl-prim-bg-60: hsl(220, 100%, 094%);
|
||||||
|
|
||||||
--cl-prim-fg-60: #1a63ff;
|
--cl-prim-fg-80: hsl(220, 100%, 050%);
|
||||||
--cl-prim-fg-80: #0051ff;
|
--cl-prim-fg-100: hsl(000, 000%, 100%);
|
||||||
--cl-prim-fg-100: #ffffff;
|
|
||||||
|
|
||||||
--cl-red-bg-100: #ffe5e5;
|
--cl-red-bg-100: hsl(000, 100%, 095%);
|
||||||
--cl-red-fg-100: #dc2626;
|
--cl-red-fg-100: hsl(000, 072%, 051%);
|
||||||
--cl-green-fg-100: #4ade80;
|
--cl-green-fg-100: hsl(120, 080%, 058%);
|
||||||
|
|
||||||
/* Dark Theme */
|
/* Dark Theme */
|
||||||
--cd-bg-120: #0d0d0d;
|
--cd-bg-120: hsl(000, 000%, 005%);
|
||||||
--cd-bg-100: #181818;
|
--cd-bg-100: hsl(000, 000%, 009%);
|
||||||
--cd-bg-80: #272727;
|
--cd-bg-80: hsl(000, 000%, 015%);
|
||||||
--cd-bg-60: #383838;
|
--cd-bg-60: hsl(000, 000%, 022%);
|
||||||
--cd-bg-40: #595959;
|
--cd-bg-40: hsl(000, 000%, 035%);
|
||||||
|
|
||||||
--cd-fg-40: #878792;
|
--cd-fg-60: hsl(000, 000%, 055%);
|
||||||
--cd-fg-60: #bcbcc2;
|
--cd-fg-80: hsl(000, 000%, 080%);
|
||||||
--cd-fg-80: #d4d4d8;
|
--cd-fg-100: hsl(000, 000%, 093%);
|
||||||
--cd-fg-100: #e4e4e7;
|
|
||||||
|
|
||||||
/* --cd-prim-bg-100: #e66000;
|
--cd-prim-bg-100: hsl(025, 079%, 052%);
|
||||||
--cd-prim-bg-80: #cc7700;
|
--cd-prim-bg-80: hsl(035, 080%, 043%);
|
||||||
--cd-prim-bg-60: #995900;
|
--cd-prim-bg-60: hsl(045, 080%, 031%);
|
||||||
|
|
||||||
--cd-prim-fg-60: #ffa666;
|
--cd-prim-fg-80: hsl(025, 080%, 050%);
|
||||||
--cd-prim-fg-80: #ff6a00;
|
--cd-prim-fg-100: hsl(000, 000%, 100%);
|
||||||
--cd-prim-fg-100: #ffffff; */
|
|
||||||
|
|
||||||
--cd-prim-bg-100: hsl(267, 90%, 60%);
|
--cd-red-bg-100: hsl(000, 100%, 015%);
|
||||||
--cd-prim-bg-80: hsl(267, 90%, 45%);
|
--cd-red-fg-100: hsl(000, 100%, 060%);
|
||||||
--cd-prim-bg-60: hsl(269, 90%, 30%);
|
--cd-green-fg-100: hsl(120, 080%, 040%);
|
||||||
|
|
||||||
--cd-prim-fg-60: hsl(267, 90%, 45%);
|
|
||||||
--cd-prim-fg-80: hsl(267, 90%, 60%);
|
|
||||||
--cd-prim-fg-100: #ffffff;
|
|
||||||
|
|
||||||
--cd-red-bg-100: #4d0000;
|
|
||||||
--cd-red-fg-100: #ff334b;
|
|
||||||
--cd-green-fg-100: #22c55e;
|
|
||||||
|
|
||||||
/* Import overrides */
|
/* Import overrides */
|
||||||
--toastify-color-dark: var(--cd-bg-60);
|
--toastify-color-dark: var(--cd-bg-60);
|
||||||
|
@ -148,7 +136,6 @@
|
||||||
.clr-footer,
|
.clr-footer,
|
||||||
.clr-modal-backdrop,
|
.clr-modal-backdrop,
|
||||||
.clr-btn-nav,
|
.clr-btn-nav,
|
||||||
.clr-checkbox,
|
|
||||||
.clr-input:disabled
|
.clr-input:disabled
|
||||||
) {
|
) {
|
||||||
background-color: var(--cl-bg-100);
|
background-color: var(--cl-bg-100);
|
||||||
|
@ -176,8 +163,7 @@
|
||||||
|
|
||||||
:is(.clr-primary,
|
:is(.clr-primary,
|
||||||
.clr-btn-primary:hover,
|
.clr-btn-primary:hover,
|
||||||
.clr-btn-primary:focus,
|
.clr-btn-primary:focus
|
||||||
.clr-checkbox:checked
|
|
||||||
) {
|
) {
|
||||||
color: var(--cl-prim-fg-100);
|
color: var(--cl-prim-fg-100);
|
||||||
background-color: var(--cl-prim-bg-100);
|
background-color: var(--cl-prim-bg-100);
|
||||||
|
@ -202,10 +188,10 @@
|
||||||
.clr-btn-default,
|
.clr-btn-default,
|
||||||
.clr-btn-primary
|
.clr-btn-primary
|
||||||
):disabled {
|
):disabled {
|
||||||
color: var(--cl-fg-60);
|
color: var(--cl-fg-80);
|
||||||
background-color: var(--cl-bg-60);
|
background-color: var(--cl-bg-60);
|
||||||
.dark & {
|
.dark & {
|
||||||
color: var(--cd-fg-60);
|
color: var(--cd-fg-80);
|
||||||
background-color: var(--cd-bg-60);
|
background-color: var(--cd-bg-60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,9 +216,9 @@
|
||||||
):focus {
|
):focus {
|
||||||
outline-width: 2px;
|
outline-width: 2px;
|
||||||
outline-style: solid;
|
outline-style: solid;
|
||||||
outline-color: var(--cl-bg-40);
|
outline-color: var(--cl-prim-bg-100);
|
||||||
.dark & {
|
.dark & {
|
||||||
outline-color: var(--cd-bg-40);
|
outline-color: var(--cd-prim-bg-100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,28 +232,28 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.clr-footer {
|
.clr-footer {
|
||||||
color: var(--cl-fg-40);
|
color: var(--cl-fg-60);
|
||||||
.dark & {
|
.dark & {
|
||||||
color: var(--cd-fg-40);
|
color: var(--cd-fg-60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.clr-btn-nav {
|
.clr-btn-nav {
|
||||||
color: var(--cl-fg-60);
|
color: var(--cl-fg-80);
|
||||||
.dark & {
|
.dark & {
|
||||||
color: var(--cd-fg-60);
|
color: var(--cd-fg-80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.clr-btn-clear {
|
.clr-btn-clear {
|
||||||
color: var(--cl-fg-60);
|
color: var(--cl-fg-80);
|
||||||
&:disabled {
|
&:disabled {
|
||||||
color: var(--cl-fg-40);
|
color: var(--cl-fg-60);
|
||||||
}
|
}
|
||||||
.dark & {
|
.dark & {
|
||||||
color: var(--cd-fg-60);
|
color: var(--cd-fg-80);
|
||||||
&:disabled {
|
&:disabled {
|
||||||
color: var(--cd-fg-40);
|
color: var(--cd-fg-60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -303,10 +289,10 @@
|
||||||
}
|
}
|
||||||
.cm-editor.cm-focused {
|
.cm-editor.cm-focused {
|
||||||
border-color: var(--cl-bg-40);
|
border-color: var(--cl-bg-40);
|
||||||
outline-color: var(--cl-bg-40);
|
outline-color: var(--cl-prim-bg-100);
|
||||||
.dark & {
|
.dark & {
|
||||||
border-color: var(--cd-bg-40);
|
border-color: var(--cd-bg-40);
|
||||||
outline-color: var(--cd-bg-40);
|
outline-color: var(--cd-prim-bg-100);
|
||||||
}
|
}
|
||||||
@apply border shadow rounded outline-2 outline
|
@apply border shadow rounded outline-2 outline
|
||||||
}
|
}
|
||||||
|
@ -314,22 +300,3 @@
|
||||||
.rdt_TableCell{
|
.rdt_TableCell{
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-dropdown-select-item {
|
|
||||||
color: var(--cl-fg-100);
|
|
||||||
border-color: var(--cl-bg-40);
|
|
||||||
background-color: var(--cl-bg-100);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: var(--cl-prim-bg-60);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark & {
|
|
||||||
color: var(--cd-fg-100);
|
|
||||||
border-color: var(--cd-bg-40);
|
|
||||||
background-color: var(--cd-bg-100);
|
|
||||||
&:hover {
|
|
||||||
background-color: var(--cd-prim-bg-60);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import BackendError from '../components/BackendError';
|
import BackendError from '../components/BackendError';
|
||||||
import Button from '../components/Common/Button';
|
import Button from '../components/Common/Button';
|
||||||
import Checkbox from '../components/Common/Checkbox';
|
import Checkbox from '../components/Common/Checkbox';
|
||||||
import FileInput from '../components/Common/FileInput';
|
|
||||||
import Form from '../components/Common/Form';
|
import Form from '../components/Common/Form';
|
||||||
|
import Label from '../components/Common/Label';
|
||||||
|
import MiniButton from '../components/Common/MiniButton';
|
||||||
import SubmitButton from '../components/Common/SubmitButton';
|
import SubmitButton from '../components/Common/SubmitButton';
|
||||||
import TextArea from '../components/Common/TextArea';
|
import TextArea from '../components/Common/TextArea';
|
||||||
import TextInput from '../components/Common/TextInput';
|
import TextInput from '../components/Common/TextInput';
|
||||||
|
import { UploadIcon } from '../components/Icons';
|
||||||
import RequireAuth from '../components/RequireAuth';
|
import RequireAuth from '../components/RequireAuth';
|
||||||
import { useLibrary } from '../context/LibraryContext';
|
import { useLibrary } from '../context/LibraryContext';
|
||||||
import { useConceptNavigation } from '../context/NagivationContext';
|
import { useConceptNavigation } from '../context/NagivationContext';
|
||||||
|
import { EXTEOR_TRS_FILE } from '../utils/constants';
|
||||||
import { IRSFormCreateData, LibraryItemType } from '../utils/models';
|
import { IRSFormCreateData, LibraryItemType } from '../utils/models';
|
||||||
|
|
||||||
function CreateRSFormPage() {
|
function CreateRSFormPage() {
|
||||||
|
@ -24,20 +27,15 @@ function CreateRSFormPage() {
|
||||||
const [alias, setAlias] = useState('');
|
const [alias, setAlias] = useState('');
|
||||||
const [comment, setComment] = useState('');
|
const [comment, setComment] = useState('');
|
||||||
const [common, setCommon] = useState(false);
|
const [common, setCommon] = useState(false);
|
||||||
const [file, setFile] = useState<File | undefined>()
|
|
||||||
|
const [fileName, setFileName] = useState('');
|
||||||
|
const [file, setFile] = useState<File | undefined>();
|
||||||
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
}, [title, alias, setError]);
|
}, [title, alias, setError]);
|
||||||
|
|
||||||
function handleFile(event: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
if (event.target.files && event.target.files.length > 0) {
|
|
||||||
setFile(event.target.files[0]);
|
|
||||||
} else {
|
|
||||||
setFile(undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCancel() {
|
function handleCancel() {
|
||||||
if (location.key !== 'default') {
|
if (location.key !== 'default') {
|
||||||
navigateHistory(-1);
|
navigateHistory(-1);
|
||||||
|
@ -67,12 +65,39 @@ function CreateRSFormPage() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleFileChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
if (event.target.files && event.target.files.length > 0) {
|
||||||
|
setFileName(event.target.files[0].name);
|
||||||
|
setFile(event.target.files[0]);
|
||||||
|
} else {
|
||||||
|
setFileName('');
|
||||||
|
setFile(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RequireAuth>
|
<RequireAuth>
|
||||||
<Form title='Создание концептуальной схемы'
|
<Form title='Создание концептуальной схемы'
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
widthClass='max-w-lg w-full mt-4'
|
widthClass='max-w-lg w-full mt-4'
|
||||||
>
|
>
|
||||||
|
<div className='relative w-full'>
|
||||||
|
<div className='absolute top-[-2.4rem] right-[-1rem] flex'>
|
||||||
|
<input
|
||||||
|
type='file'
|
||||||
|
ref={inputRef}
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
accept={EXTEOR_TRS_FILE}
|
||||||
|
onChange={handleFileChange}
|
||||||
|
/>
|
||||||
|
<MiniButton
|
||||||
|
tooltip='Загрузить из Экстеор'
|
||||||
|
icon={<UploadIcon size={5} color='text-primary'/>}
|
||||||
|
onClick={() => inputRef.current?.click()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{ fileName && <Label text={`Загружен файл: ${fileName}`} />}
|
||||||
<TextInput id='title' label='Полное название' type='text'
|
<TextInput id='title' label='Полное название' type='text'
|
||||||
required={!file}
|
required={!file}
|
||||||
placeholder={file && 'Загрузить из файла'}
|
placeholder={file && 'Загрузить из файла'}
|
||||||
|
@ -93,13 +118,8 @@ function CreateRSFormPage() {
|
||||||
/>
|
/>
|
||||||
<Checkbox id='common' label='Общедоступная схема'
|
<Checkbox id='common' label='Общедоступная схема'
|
||||||
value={common}
|
value={common}
|
||||||
onChange={event => setCommon(event.target.checked)}
|
setValue={value => setCommon(value ?? false)}
|
||||||
/>
|
/>
|
||||||
<FileInput id='trs' label='Загрузить из Экстеор'
|
|
||||||
acceptType='.trs'
|
|
||||||
onChange={handleFile}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className='flex items-center justify-center gap-4 py-2 mt-4'>
|
<div className='flex items-center justify-center gap-4 py-2 mt-4'>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
text='Создать схему'
|
text='Создать схему'
|
||||||
|
|
|
@ -36,38 +36,38 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
||||||
{ pickerMenu.isActive &&
|
{ pickerMenu.isActive &&
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<DropdownCheckbox
|
<DropdownCheckbox
|
||||||
onChange={() => handleChange(LibraryFilterStrategy.MANUAL)}
|
setValue={() => handleChange(LibraryFilterStrategy.MANUAL)}
|
||||||
value={value === LibraryFilterStrategy.MANUAL}
|
value={value === LibraryFilterStrategy.MANUAL}
|
||||||
label='Отображать все'
|
label='Отображать все'
|
||||||
/>
|
/>
|
||||||
<DropdownCheckbox
|
<DropdownCheckbox
|
||||||
onChange={() => handleChange(LibraryFilterStrategy.COMMON)}
|
setValue={() => handleChange(LibraryFilterStrategy.COMMON)}
|
||||||
value={value === LibraryFilterStrategy.COMMON}
|
value={value === LibraryFilterStrategy.COMMON}
|
||||||
label='Общедоступные'
|
label='Общедоступные'
|
||||||
tooltip='Отображать только общедоступные схемы'
|
tooltip='Отображать только общедоступные схемы'
|
||||||
/>
|
/>
|
||||||
<DropdownCheckbox
|
<DropdownCheckbox
|
||||||
onChange={() => handleChange(LibraryFilterStrategy.CANONICAL)}
|
setValue={() => handleChange(LibraryFilterStrategy.CANONICAL)}
|
||||||
value={value === LibraryFilterStrategy.CANONICAL}
|
value={value === LibraryFilterStrategy.CANONICAL}
|
||||||
label='Неизменные'
|
label='Неизменные'
|
||||||
tooltip='Отображать только стандартные схемы'
|
tooltip='Отображать только стандартные схемы'
|
||||||
/>
|
/>
|
||||||
<DropdownCheckbox
|
<DropdownCheckbox
|
||||||
onChange={() => handleChange(LibraryFilterStrategy.PERSONAL)}
|
setValue={() => handleChange(LibraryFilterStrategy.PERSONAL)}
|
||||||
value={value === LibraryFilterStrategy.PERSONAL}
|
value={value === LibraryFilterStrategy.PERSONAL}
|
||||||
label='Личные'
|
label='Личные'
|
||||||
disabled={!user}
|
disabled={!user}
|
||||||
tooltip='Отображать только подписки и владеемые схемы'
|
tooltip='Отображать только подписки и владеемые схемы'
|
||||||
/>
|
/>
|
||||||
<DropdownCheckbox
|
<DropdownCheckbox
|
||||||
onChange={() => handleChange(LibraryFilterStrategy.SUBSCRIBE)}
|
setValue={() => handleChange(LibraryFilterStrategy.SUBSCRIBE)}
|
||||||
value={value === LibraryFilterStrategy.SUBSCRIBE}
|
value={value === LibraryFilterStrategy.SUBSCRIBE}
|
||||||
label='Подписки'
|
label='Подписки'
|
||||||
disabled={!user}
|
disabled={!user}
|
||||||
tooltip='Отображать только подписки'
|
tooltip='Отображать только подписки'
|
||||||
/>
|
/>
|
||||||
<DropdownCheckbox
|
<DropdownCheckbox
|
||||||
onChange={() => handleChange(LibraryFilterStrategy.OWNED)}
|
setValue={() => handleChange(LibraryFilterStrategy.OWNED)}
|
||||||
value={value === LibraryFilterStrategy.OWNED}
|
value={value === LibraryFilterStrategy.OWNED}
|
||||||
disabled={!user}
|
disabled={!user}
|
||||||
label='Я - Владелец!'
|
label='Я - Владелец!'
|
||||||
|
|
|
@ -79,7 +79,7 @@ function DlgCloneRSForm({ hideWindow }: DlgCloneRSFormProps) {
|
||||||
/>
|
/>
|
||||||
<Checkbox id='common' label='Общедоступная схема'
|
<Checkbox id='common' label='Общедоступная схема'
|
||||||
value={common}
|
value={common}
|
||||||
onChange={event => setCommon(event.target.checked)}
|
setValue={value => setCommon(value)}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import ConceptSelect from '../../components/Common/ConceptSelect';
|
import ConceptSelectSingle from '../../components/Common/ConceptSelectSingle';
|
||||||
import Modal from '../../components/Common/Modal';
|
import Modal from '../../components/Common/Modal';
|
||||||
import TextArea from '../../components/Common/TextArea';
|
import TextArea from '../../components/Common/TextArea';
|
||||||
import RSInput from '../../components/RSInput';
|
import RSInput from '../../components/RSInput';
|
||||||
|
@ -57,12 +57,12 @@ function DlgCreateCst({ hideWindow, initial, onCreate }: DlgCreateCstProps) {
|
||||||
>
|
>
|
||||||
<div className='h-fit w-[35rem] px-2 mb-2 flex flex-col justify-stretch'>
|
<div className='h-fit w-[35rem] px-2 mb-2 flex flex-col justify-stretch'>
|
||||||
<div className='flex justify-center w-full'>
|
<div className='flex justify-center w-full'>
|
||||||
<ConceptSelect
|
<ConceptSelectSingle
|
||||||
className='my-2 min-w-[15rem] self-center'
|
className='my-2 min-w-[15rem] self-center'
|
||||||
options={CstTypeSelector}
|
options={CstTypeSelector}
|
||||||
placeholder='Выберите тип'
|
placeholder='Выберите тип'
|
||||||
values={selectedType ? [{ value: selectedType, label: getCstTypeLabel(selectedType) }] : []}
|
value={selectedType ? { value: selectedType, label: getCstTypeLabel(selectedType) } : null}
|
||||||
onChange={data => setSelectedType(data.length > 0 ? data[0].value : CstType.BASE)}
|
onChange={data => setSelectedType(data?.value ?? CstType.BASE)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<TextArea id='term' label='Термин'
|
<TextArea id='term' label='Термин'
|
||||||
|
@ -72,12 +72,14 @@ function DlgCreateCst({ hideWindow, initial, onCreate }: DlgCreateCstProps) {
|
||||||
spellCheck
|
spellCheck
|
||||||
onChange={event => setTerm(event.target.value)}
|
onChange={event => setTerm(event.target.value)}
|
||||||
/>
|
/>
|
||||||
|
<div className='mt-3'>
|
||||||
<RSInput id='expression' label='Формальное выражение'
|
<RSInput id='expression' label='Формальное выражение'
|
||||||
editable
|
editable
|
||||||
height='5.5rem'
|
height='5.5rem'
|
||||||
value={expression}
|
value={expression}
|
||||||
onChange={value => setExpression(value)}
|
onChange={value => setExpression(value)}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
<TextArea id='definition' label='Текстовое определение'
|
<TextArea id='definition' label='Текстовое определение'
|
||||||
placeholder='Лингвистическая интерпретация формального выражения'
|
placeholder='Лингвистическая интерпретация формального выражения'
|
||||||
rows={2}
|
rows={2}
|
||||||
|
|
|
@ -52,7 +52,7 @@ function DlgDeleteCst({ hideWindow, selected, onDelete }: DlgDeleteCstProps) {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Удалить зависимые конституенты'
|
label='Удалить зависимые конституенты'
|
||||||
value={expandOut}
|
value={expandOut}
|
||||||
onChange={data => setExpandOut(data.target.checked)}
|
setValue={value => setExpandOut(value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -81,25 +81,25 @@ function DlgGraphOptions({ hideWindow, initial, onConfirm }:DlgGraphOptionsProps
|
||||||
label='Скрыть текст'
|
label='Скрыть текст'
|
||||||
tooltip='Не отображать термины'
|
tooltip='Не отображать термины'
|
||||||
value={noTerms}
|
value={noTerms}
|
||||||
onChange={ event => setNoTerms(event.target.checked) }
|
setValue={ value => setNoTerms(value) }
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Скрыть несвязанные'
|
label='Скрыть несвязанные'
|
||||||
tooltip='Неиспользуемые конституенты'
|
tooltip='Неиспользуемые конституенты'
|
||||||
value={noHermits}
|
value={noHermits}
|
||||||
onChange={ event => setNoHermits(event.target.checked) }
|
setValue={ value => setNoHermits(value) }
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Скрыть шаблоны'
|
label='Скрыть шаблоны'
|
||||||
tooltip='Терм-функции и предикат-функции с параметризованными аргументами'
|
tooltip='Терм-функции и предикат-функции с параметризованными аргументами'
|
||||||
value={noTemplates}
|
value={noTemplates}
|
||||||
onChange={ event => setNoTemplates(event.target.checked) }
|
setValue={ value => setNoTemplates(value) }
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Транзитивная редукция'
|
label='Транзитивная редукция'
|
||||||
tooltip='Удалить связи, образующие транзитивные пути в графе'
|
tooltip='Удалить связи, образующие транзитивные пути в графе'
|
||||||
value={noTransitive}
|
value={noTransitive}
|
||||||
onChange={ event => setNoTransitive(event.target.checked) }
|
setValue={ value => setNoTransitive(value) }
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-col'>
|
<div className='flex flex-col'>
|
||||||
|
@ -107,42 +107,42 @@ function DlgGraphOptions({ hideWindow, initial, onConfirm }:DlgGraphOptionsProps
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={getCstTypeLabel(CstType.BASE)}
|
label={getCstTypeLabel(CstType.BASE)}
|
||||||
value={allowBase}
|
value={allowBase}
|
||||||
onChange={ event => setAllowBase(event.target.checked) }
|
setValue={ value => setAllowBase(value) }
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={getCstTypeLabel(CstType.STRUCTURED)}
|
label={getCstTypeLabel(CstType.STRUCTURED)}
|
||||||
value={allowStruct}
|
value={allowStruct}
|
||||||
onChange={ event => setAllowStruct(event.target.checked) }
|
setValue={ value => setAllowStruct(value) }
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={getCstTypeLabel(CstType.TERM)}
|
label={getCstTypeLabel(CstType.TERM)}
|
||||||
value={allowTerm}
|
value={allowTerm}
|
||||||
onChange={ event => setAllowTerm(event.target.checked) }
|
setValue={ value => setAllowTerm(value) }
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={getCstTypeLabel(CstType.AXIOM)}
|
label={getCstTypeLabel(CstType.AXIOM)}
|
||||||
value={allowAxiom}
|
value={allowAxiom}
|
||||||
onChange={ event => setAllowAxiom(event.target.checked) }
|
setValue={ value => setAllowAxiom(value) }
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={getCstTypeLabel(CstType.FUNCTION)}
|
label={getCstTypeLabel(CstType.FUNCTION)}
|
||||||
value={allowFunction}
|
value={allowFunction}
|
||||||
onChange={ event => setAllowFunction(event.target.checked) }
|
setValue={ value => setAllowFunction(value) }
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={getCstTypeLabel(CstType.PREDICATE)}
|
label={getCstTypeLabel(CstType.PREDICATE)}
|
||||||
value={allowPredicate}
|
value={allowPredicate}
|
||||||
onChange={ event => setAllowPredicate(event.target.checked) }
|
setValue={ value => setAllowPredicate(value) }
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={getCstTypeLabel(CstType.CONSTANT)}
|
label={getCstTypeLabel(CstType.CONSTANT)}
|
||||||
value={allowConstant}
|
value={allowConstant}
|
||||||
onChange={ event => setAllowConstant(event.target.checked) }
|
setValue={ value => setAllowConstant(value) }
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={getCstTypeLabel(CstType.THEOREM)}
|
label={getCstTypeLabel(CstType.THEOREM)}
|
||||||
value={allowTheorem}
|
value={allowTheorem}
|
||||||
onChange={ event => setAllowTheorem(event.target.checked) }
|
setValue ={ value => setAllowTheorem(value) }
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { useLayoutEffect, useState } from 'react';
|
import { useLayoutEffect, useState } from 'react';
|
||||||
|
|
||||||
import ConceptSelect from '../../components/Common/ConceptSelect';
|
import ConceptSelectSingle from '../../components/Common/ConceptSelectSingle';
|
||||||
import Modal from '../../components/Common/Modal';
|
import Modal from '../../components/Common/Modal';
|
||||||
import TextInput from '../../components/Common/TextInput';
|
import TextInput from '../../components/Common/TextInput';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
|
@ -67,12 +67,12 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
|
||||||
submitText='Переименовать'
|
submitText='Переименовать'
|
||||||
>
|
>
|
||||||
<div className='flex items-center gap-4 px-2 my-2 h-fit min-w-[25rem]'>
|
<div className='flex items-center gap-4 px-2 my-2 h-fit min-w-[25rem]'>
|
||||||
<ConceptSelect
|
<ConceptSelectSingle
|
||||||
className='min-w-[14rem] self-center'
|
className='min-w-[14rem] self-center'
|
||||||
options={CstTypeSelector}
|
options={CstTypeSelector}
|
||||||
placeholder='Выберите тип'
|
placeholder='Выберите тип'
|
||||||
values={cstType ? [{ value: cstType, label: getCstTypeLabel(cstType) }] : []}
|
value={cstType ? { value: cstType, label: getCstTypeLabel(cstType) } : null}
|
||||||
onChange={data => setCstType(data.length > 0 ? data[0].value : CstType.BASE)}
|
onChange={data => setCstType(data?.value ?? CstType.BASE)}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<TextInput id='alias' label='Имя'
|
<TextInput id='alias' label='Имя'
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Checkbox from '../../components/Common/Checkbox';
|
||||||
import FileInput from '../../components/Common/FileInput';
|
import FileInput from '../../components/Common/FileInput';
|
||||||
import Modal from '../../components/Common/Modal';
|
import Modal from '../../components/Common/Modal';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
|
import { EXTEOR_TRS_FILE } from '../../utils/constants';
|
||||||
import { IRSFormUploadData } from '../../utils/models';
|
import { IRSFormUploadData } from '../../utils/models';
|
||||||
|
|
||||||
interface DlgUploadRSFormProps {
|
interface DlgUploadRSFormProps {
|
||||||
|
@ -44,16 +45,17 @@ function DlgUploadRSForm({ hideWindow }: DlgUploadRSFormProps) {
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
submitText='Загрузить'
|
submitText='Загрузить'
|
||||||
>
|
>
|
||||||
<div className='max-w-[20rem]'>
|
<div className='flex flex-col items-center'>
|
||||||
<FileInput
|
<FileInput
|
||||||
label='Выбрать файл'
|
label='Выбрать файл'
|
||||||
acceptType='.trs'
|
acceptType={EXTEOR_TRS_FILE}
|
||||||
onChange={handleFile}
|
onChange={handleFile}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Загружать название и комментарий'
|
label='Загружать название и комментарий'
|
||||||
value={loadMetadata}
|
value={loadMetadata}
|
||||||
onChange={event => setLoadMetadata(event.target.checked)}
|
setValue={value => setLoadMetadata(value)}
|
||||||
|
widthClass='w-fit pb-2'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -136,14 +136,14 @@ function EditorRSForm({ onDestroy, onClaim, onShare, isModified, setIsModified,
|
||||||
value={common}
|
value={common}
|
||||||
widthClass='w-fit mt-3'
|
widthClass='w-fit mt-3'
|
||||||
disabled={!isEditable}
|
disabled={!isEditable}
|
||||||
onChange={event => setCommon(event.target.checked)}
|
setValue={value => setCommon(value)}
|
||||||
/>
|
/>
|
||||||
<Checkbox id='canonical' label='Неизменная схема'
|
<Checkbox id='canonical' label='Неизменная схема'
|
||||||
widthClass='w-fit'
|
widthClass='w-fit'
|
||||||
value={canonical}
|
value={canonical}
|
||||||
tooltip='Только администраторы могут присваивать схемам неизменный статус'
|
tooltip='Только администраторы могут присваивать схемам неизменный статус'
|
||||||
disabled={!isEditable || !isForceAdmin}
|
disabled={!isEditable || !isForceAdmin}
|
||||||
onChange={event => setCanonical(event.target.checked)}
|
setValue={value => setCanonical(value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { GraphCanvas, GraphCanvasRef, GraphEdge,
|
||||||
|
|
||||||
import Button from '../../components/Common/Button';
|
import Button from '../../components/Common/Button';
|
||||||
import Checkbox from '../../components/Common/Checkbox';
|
import Checkbox from '../../components/Common/Checkbox';
|
||||||
import ConceptSelect from '../../components/Common/ConceptSelect';
|
import ConceptSelectSingle from '../../components/Common/ConceptSelectSingle';
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
||||||
import Divider from '../../components/Common/Divider';
|
import Divider from '../../components/Common/Divider';
|
||||||
import MiniButton from '../../components/Common/MiniButton';
|
import MiniButton from '../../components/Common/MiniButton';
|
||||||
|
@ -16,7 +16,7 @@ import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import useLocalStorage from '../../hooks/useLocalStorage';
|
import useLocalStorage from '../../hooks/useLocalStorage';
|
||||||
import { graphDarkT, graphLightT, IColorTheme } from '../../utils/color';
|
import { graphDarkT, graphLightT, IColorTheme } from '../../utils/color';
|
||||||
import { prefixes, resources } from '../../utils/constants';
|
import { prefixes, resources, TIMEOUT_GRAPH_REFRESH } from '../../utils/constants';
|
||||||
import { Graph } from '../../utils/Graph';
|
import { Graph } from '../../utils/Graph';
|
||||||
import { CstType, IConstituenta, ICstCreateData } from '../../utils/models';
|
import { CstType, IConstituenta, ICstCreateData } from '../../utils/models';
|
||||||
import { getCstClassColor, getCstStatusColor,
|
import { getCstClassColor, getCstStatusColor,
|
||||||
|
@ -220,9 +220,9 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
||||||
}, [selectedDismissed, selections]);
|
}, [selectedDismissed, selections]);
|
||||||
const nothingSelected = useMemo(() => allSelected.length === 0, [allSelected]);
|
const nothingSelected = useMemo(() => allSelected.length === 0, [allSelected]);
|
||||||
|
|
||||||
const handleRecreate = useCallback(
|
const handleResetViewpoint = useCallback(
|
||||||
() => {
|
() => {
|
||||||
graphRef.current?.resetControls();
|
graphRef.current?.resetControls(true);
|
||||||
graphRef.current?.centerGraph();
|
graphRef.current?.centerGraph();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -293,6 +293,16 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleChangeLayout(newLayout: LayoutTypes) {
|
||||||
|
if (newLayout === layout) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLayout(newLayout);
|
||||||
|
setTimeout(() => {
|
||||||
|
handleResetViewpoint();
|
||||||
|
}, TIMEOUT_GRAPH_REFRESH);
|
||||||
|
}
|
||||||
|
|
||||||
function getOptions() {
|
function getOptions() {
|
||||||
return {
|
return {
|
||||||
noHermits: noHermits,
|
noHermits: noHermits,
|
||||||
|
@ -394,39 +404,39 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
||||||
widthClass='h-full'
|
widthClass='h-full'
|
||||||
onClick={() => setShowOptions(true)}
|
onClick={() => setShowOptions(true)}
|
||||||
/>
|
/>
|
||||||
<ConceptSelect
|
<ConceptSelectSingle
|
||||||
className='min-w-[9.8rem]'
|
className='min-w-[9.8rem]'
|
||||||
options={GraphColoringSelector}
|
options={GraphColoringSelector}
|
||||||
searchable={false}
|
isSearchable={false}
|
||||||
placeholder='Выберите цвет'
|
placeholder='Выберите цвет'
|
||||||
values={coloringScheme ? [{ value: coloringScheme, label: mapColoringLabels.get(coloringScheme) }] : []}
|
value={coloringScheme ? { value: coloringScheme, label: mapColoringLabels.get(coloringScheme) } : null}
|
||||||
onChange={data => setColoringScheme(data.length > 0 ? data[0].value : GraphColoringSelector[0].value)}
|
onChange={data => setColoringScheme(data?.value ?? GraphColoringSelector[0].value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<ConceptSelect
|
<ConceptSelectSingle
|
||||||
className='w-full mt-1'
|
className='w-full mt-1'
|
||||||
options={GraphLayoutSelector}
|
options={GraphLayoutSelector}
|
||||||
searchable={false}
|
isSearchable={false}
|
||||||
placeholder='Способ расположения'
|
placeholder='Способ расположения'
|
||||||
values={layout ? [{ value: layout, label: mapLayoutLabels.get(layout) }] : []}
|
value={layout ? { value: layout, label: mapLayoutLabels.get(layout) } : null}
|
||||||
onChange={data => setLayout(data.length > 0 ? data[0].value : GraphLayoutSelector[0].value)}
|
onChange={data => handleChangeLayout(data?.value ?? GraphLayoutSelector[0].value)}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Скрыть текст'
|
label='Скрыть текст'
|
||||||
value={noTerms}
|
value={noTerms}
|
||||||
onChange={ event => setNoTerms(event.target.checked) }
|
setValue={ value => setNoTerms(value) }
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Транзитивная редукция'
|
label='Транзитивная редукция'
|
||||||
value={noTransitive}
|
value={noTransitive}
|
||||||
onChange={ event => setNoTransitive(event.target.checked) }
|
setValue={ value => setNoTransitive(value) }
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
disabled={!is3D}
|
disabled={!is3D}
|
||||||
label='Анимация вращения'
|
label='Анимация вращения'
|
||||||
value={orbit}
|
value={orbit}
|
||||||
onChange={ event => setOrbit(event.target.checked) }
|
setValue={ value => setOrbit(value) }
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Divider margins='mt-3 mb-2' />
|
<Divider margins='mt-3 mb-2' />
|
||||||
|
@ -471,8 +481,8 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
||||||
</div>
|
</div>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
icon={<ArrowsRotateIcon size={5} />}
|
icon={<ArrowsRotateIcon size={5} />}
|
||||||
tooltip='Пересоздать граф'
|
tooltip='Восстановить камеру'
|
||||||
onClick={handleRecreate}
|
onClick={handleResetViewpoint}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ConceptTooltip anchorSelect='#items-graph-help'>
|
<ConceptTooltip anchorSelect='#items-graph-help'>
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { useConceptNavigation } from '../../context/NagivationContext';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import useModificationPrompt from '../../hooks/useModificationPrompt';
|
import useModificationPrompt from '../../hooks/useModificationPrompt';
|
||||||
import { prefixes, TIMEOUT_UI_REFRESH } from '../../utils/constants';
|
import { EXTEOR_TRS_FILE, prefixes, TIMEOUT_UI_REFRESH } from '../../utils/constants';
|
||||||
import { ICstCreateData, ICstRenameData, SyntaxTree } from '../../utils/models';
|
import { ICstCreateData, ICstRenameData, SyntaxTree } from '../../utils/models';
|
||||||
import { createAliasFor } from '../../utils/staticUI';
|
import { createAliasFor } from '../../utils/staticUI';
|
||||||
import DlgCloneRSForm from './DlgCloneRSForm';
|
import DlgCloneRSForm from './DlgCloneRSForm';
|
||||||
|
@ -256,7 +256,7 @@ function RSTabs() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const fileName = (schema?.alias ?? 'Schema') + '.trs';
|
const fileName = (schema?.alias ?? 'Schema') + EXTEOR_TRS_FILE;
|
||||||
download(
|
download(
|
||||||
(data) => {
|
(data) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -146,14 +146,14 @@ function RSTabsMenu({
|
||||||
{(isOwned || user?.is_staff) &&
|
{(isOwned || user?.is_staff) &&
|
||||||
<DropdownCheckbox
|
<DropdownCheckbox
|
||||||
value={isReadonly}
|
value={isReadonly}
|
||||||
onChange={toggleReadonly}
|
setValue={toggleReadonly}
|
||||||
label='Я — читатель!'
|
label='Я — читатель!'
|
||||||
tooltip='Режим чтения'
|
tooltip='Режим чтения'
|
||||||
/>}
|
/>}
|
||||||
{user?.is_staff &&
|
{user?.is_staff &&
|
||||||
<DropdownCheckbox
|
<DropdownCheckbox
|
||||||
value={isForceAdmin}
|
value={isForceAdmin}
|
||||||
onChange={toggleForceAdmin}
|
setValue={toggleForceAdmin}
|
||||||
label='Я — администратор!'
|
label='Я — администратор!'
|
||||||
tooltip='Режим редактирования для администраторов'
|
tooltip='Режим редактирования для администраторов'
|
||||||
/>}
|
/>}
|
||||||
|
|
|
@ -1,70 +1,107 @@
|
||||||
export interface IColorTheme {
|
// =========== Modules contains all dynamic color definitions ==========
|
||||||
red: string
|
|
||||||
green: string
|
|
||||||
blue: string
|
|
||||||
teal: string
|
|
||||||
orange: string
|
|
||||||
|
|
||||||
|
|
||||||
|
// ============= MAIN COLOR THEMES ==========
|
||||||
|
export interface IColorTheme {
|
||||||
bgDefault: string
|
bgDefault: string
|
||||||
bgInput: string
|
bgInput: string
|
||||||
bgControls: string
|
bgControls: string
|
||||||
bgDisabled: string
|
bgDisabled: string
|
||||||
bgHover: string
|
bgPrimary: string
|
||||||
bgSelected: string
|
bgSelected: string
|
||||||
|
bgHover: string
|
||||||
bgWarning: string
|
bgWarning: string
|
||||||
|
|
||||||
border: string
|
border: string
|
||||||
|
|
||||||
fgDefault: string
|
fgDefault: string
|
||||||
|
fgSelected: string
|
||||||
fgDisabled: string
|
fgDisabled: string
|
||||||
fgWarning: string
|
fgWarning: string
|
||||||
|
|
||||||
|
// Hightlight syntax accents
|
||||||
|
bgRed: string
|
||||||
|
bgGreen: string
|
||||||
|
bgBlue: string
|
||||||
|
bgPurple: string
|
||||||
|
bgTeal: string
|
||||||
|
bgOrange: string
|
||||||
|
|
||||||
|
fgRed: string
|
||||||
|
fgGreen: string
|
||||||
|
fgBlue: string
|
||||||
|
fgPurple: string
|
||||||
|
fgTeal: string
|
||||||
|
fgOrange: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========== GENERAL THEMES =========
|
// ======= Light =======
|
||||||
export const lightT: IColorTheme = {
|
export const lightT: IColorTheme = {
|
||||||
red: '#ffc9c9',
|
|
||||||
green: '#aaff80',
|
|
||||||
blue: '#b3bdff',
|
|
||||||
teal: '#a5e9fa',
|
|
||||||
orange: '#ffbb80',
|
|
||||||
|
|
||||||
bgDefault: 'var(--cl-bg-100)',
|
bgDefault: 'var(--cl-bg-100)',
|
||||||
bgInput: 'var(--cl-bg-120)',
|
bgInput: 'var(--cl-bg-120)',
|
||||||
bgControls: 'var(--cl-bg-80)',
|
bgControls: 'var(--cl-bg-80)',
|
||||||
bgDisabled: 'var(--cl-bg-60)',
|
bgDisabled: 'var(--cl-bg-60)',
|
||||||
bgHover: 'var(--cl-prim-bg-60)',
|
bgPrimary: 'var(--cl-prim-bg-100)',
|
||||||
bgSelected: 'var(--cl-prim-bg-80)',
|
bgSelected: 'var(--cl-prim-bg-80)',
|
||||||
|
bgHover: 'var(--cl-prim-bg-60)',
|
||||||
bgWarning: 'var(--cl-red-bg-100)',
|
bgWarning: 'var(--cl-red-bg-100)',
|
||||||
|
|
||||||
border: 'var(--cl-bg-40)',
|
border: 'var(--cl-bg-40)',
|
||||||
|
|
||||||
fgDefault: 'var(--cl-fg-100)',
|
fgDefault: 'var(--cl-fg-100)',
|
||||||
fgDisabled: 'var(--cl-fg-60)',
|
fgSelected: 'var(--cl-fg-100)',
|
||||||
fgWarning: 'var(--cl-red-fg-100)'
|
fgDisabled: 'var(--cl-fg-80)',
|
||||||
|
fgWarning: 'var(--cl-red-fg-100)',
|
||||||
|
|
||||||
|
// Hightlight syntax accents
|
||||||
|
bgRed: 'hsl(000, 100%, 089%)',
|
||||||
|
bgGreen: 'hsl(100, 100%, 075%)',
|
||||||
|
bgBlue: 'hsl(235, 100%, 085%)',
|
||||||
|
bgPurple: 'hsl(274, 089%, 081%)',
|
||||||
|
bgTeal: 'hsl(192, 089%, 081%)',
|
||||||
|
bgOrange: 'hsl(028, 100%, 075%)',
|
||||||
|
|
||||||
|
fgRed: 'hsl(000, 090%, 045%)',
|
||||||
|
fgGreen: 'hsl(100, 090%, 035%)',
|
||||||
|
fgBlue: 'hsl(235, 100%, 050%)',
|
||||||
|
fgPurple: 'hsl(270, 100%, 070%)',
|
||||||
|
fgTeal: 'hsl(192, 090%, 040%)',
|
||||||
|
fgOrange: 'hsl(030, 090%, 055%)'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ======= DARK ========
|
||||||
export const darkT: IColorTheme = {
|
export const darkT: IColorTheme = {
|
||||||
red: '#bf0d00',
|
|
||||||
green: '#2b8000',
|
|
||||||
blue: '#394bbf',
|
|
||||||
teal: '#007a99',
|
|
||||||
orange: '#964600',
|
|
||||||
|
|
||||||
bgDefault: 'var(--cd-bg-100)',
|
bgDefault: 'var(--cd-bg-100)',
|
||||||
bgInput: 'var(--cd-bg-120)',
|
bgInput: 'var(--cd-bg-120)',
|
||||||
bgControls: 'var(--cd-bg-80)',
|
bgControls: 'var(--cd-bg-80)',
|
||||||
bgDisabled: 'var(--cd-bg-60)',
|
bgDisabled: 'var(--cd-bg-60)',
|
||||||
bgHover: 'var(--cd-prim-bg-60)',
|
bgPrimary: 'var(--cd-prim-bg-100)',
|
||||||
bgSelected: 'var(--cd-prim-bg-80)',
|
bgSelected: 'var(--cd-prim-bg-80)',
|
||||||
|
bgHover: 'var(--cd-prim-bg-60)',
|
||||||
bgWarning: 'var(--cd-red-bg-100)',
|
bgWarning: 'var(--cd-red-bg-100)',
|
||||||
|
|
||||||
border: 'var(--cd-bg-40)',
|
border: 'var(--cd-bg-40)',
|
||||||
|
|
||||||
fgDefault: 'var(--cd-fg-100)',
|
fgDefault: 'var(--cd-fg-100)',
|
||||||
fgDisabled: 'var(--cd-fg-60)',
|
fgSelected: 'var(--cd-fg-100)',
|
||||||
fgWarning: 'var(--cd-red-fg-100)'
|
fgDisabled: 'var(--cd-fg-80)',
|
||||||
};
|
fgWarning: 'var(--cd-red-fg-100)',
|
||||||
|
|
||||||
|
// Hightlight syntax accents
|
||||||
|
bgRed: 'hsl(000, 080%, 037%)',
|
||||||
|
bgGreen: 'hsl(100, 080%, 025%)',
|
||||||
|
bgBlue: 'hsl(235, 054%, 049%)',
|
||||||
|
bgPurple: 'hsl(270, 080%, 050%)',
|
||||||
|
bgTeal: 'hsl(192, 080%, 030%)',
|
||||||
|
bgOrange: 'hsl(035, 100%, 035%)',
|
||||||
|
|
||||||
|
fgRed: 'hsl(000, 080%, 050%)',
|
||||||
|
fgGreen: 'hsl(100, 080%, 040%)',
|
||||||
|
fgBlue: 'hsl(235, 100%, 080%)',
|
||||||
|
fgPurple: 'hsl(270, 100%, 080%)',
|
||||||
|
fgTeal: 'hsl(192, 100%, 030%)',
|
||||||
|
fgOrange: 'hsl(035, 100%, 050%)'
|
||||||
|
};
|
||||||
|
|
||||||
// ========= DATA TABLE THEMES ========
|
// ========= DATA TABLE THEMES ========
|
||||||
export const dataTableLightT = {
|
export const dataTableLightT = {
|
||||||
|
@ -119,6 +156,51 @@ export const dataTableDarkT = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ============ SELECT THEMES ==========
|
||||||
|
export const selectLightT = {
|
||||||
|
primary: lightT.bgPrimary,
|
||||||
|
primary75: lightT.bgSelected,
|
||||||
|
primary50: lightT.bgHover,
|
||||||
|
primary25: lightT.bgHover,
|
||||||
|
|
||||||
|
danger: lightT.fgWarning,
|
||||||
|
dangerLight: lightT.bgWarning,
|
||||||
|
|
||||||
|
neutral0: lightT.bgInput,
|
||||||
|
neutral5: lightT.bgDefault,
|
||||||
|
neutral10: lightT.border,
|
||||||
|
neutral20: lightT.border,
|
||||||
|
neutral30: lightT.border,
|
||||||
|
neutral40: lightT.fgDisabled,
|
||||||
|
neutral50: lightT.fgWarning,
|
||||||
|
neutral60: lightT.fgDefault,
|
||||||
|
neutral70: lightT.fgWarning,
|
||||||
|
neutral80: lightT.fgDefault,
|
||||||
|
neutral90: lightT.fgWarning
|
||||||
|
}
|
||||||
|
|
||||||
|
export const selectDarkT = {
|
||||||
|
primary: darkT.bgPrimary,
|
||||||
|
primary75: darkT.bgSelected,
|
||||||
|
primary50: darkT.bgHover,
|
||||||
|
primary25: darkT.bgHover,
|
||||||
|
|
||||||
|
danger: darkT.fgWarning,
|
||||||
|
dangerLight: darkT.bgWarning,
|
||||||
|
|
||||||
|
neutral0: darkT.bgInput,
|
||||||
|
neutral5: darkT.bgDefault,
|
||||||
|
neutral10: darkT.border,
|
||||||
|
neutral20: darkT.border,
|
||||||
|
neutral30: darkT.border,
|
||||||
|
neutral40: darkT.fgDisabled,
|
||||||
|
neutral50: darkT.fgWarning,
|
||||||
|
neutral60: darkT.fgDefault,
|
||||||
|
neutral70: darkT.fgWarning,
|
||||||
|
neutral80: darkT.fgDefault,
|
||||||
|
neutral90: darkT.fgWarning
|
||||||
|
}
|
||||||
|
|
||||||
// ============ GRAPH THEMES ==========
|
// ============ GRAPH THEMES ==========
|
||||||
export const graphLightT = {
|
export const graphLightT = {
|
||||||
canvas: {
|
canvas: {
|
||||||
|
@ -221,20 +303,22 @@ export const graphDarkT = {
|
||||||
// ======== Bracket Matching Themes ===========
|
// ======== Bracket Matching Themes ===========
|
||||||
export const bracketsLightT = {
|
export const bracketsLightT = {
|
||||||
'.cc-nonmatchingBracket': {
|
'.cc-nonmatchingBracket': {
|
||||||
color: lightT.fgWarning,
|
color: lightT.fgRed,
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
},
|
},
|
||||||
'&.cm-focused .cc-matchingBracket': {
|
'&.cm-focused .cc-matchingBracket': {
|
||||||
backgroundColor: lightT.bgSelected,
|
backgroundColor: lightT.bgSelected,
|
||||||
|
color: lightT.fgSelected
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const bracketsDarkT = {
|
export const bracketsDarkT = {
|
||||||
'.cc-nonmatchingBracket': {
|
'.cc-nonmatchingBracket': {
|
||||||
color: darkT.fgWarning,
|
color: darkT.fgRed,
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
},
|
},
|
||||||
'&.cm-focused .cc-matchingBracket': {
|
'&.cm-focused .cc-matchingBracket': {
|
||||||
backgroundColor: darkT.bgSelected,
|
backgroundColor: darkT.bgSelected,
|
||||||
|
color: darkT.fgSelected
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,9 @@ export const config = {
|
||||||
backend: import.meta.env.VITE_PORTAL_BACKEND as string
|
backend: import.meta.env.VITE_PORTAL_BACKEND as string
|
||||||
};
|
};
|
||||||
export const TIMEOUT_UI_REFRESH = 100;
|
export const TIMEOUT_UI_REFRESH = 100;
|
||||||
|
export const TIMEOUT_GRAPH_REFRESH = 200;
|
||||||
|
|
||||||
|
export const EXTEOR_TRS_FILE = '.trs';
|
||||||
|
|
||||||
export const youtube = {
|
export const youtube = {
|
||||||
intro: '0Ty9mu9sOJo'
|
intro: '0Ty9mu9sOJo'
|
||||||
|
|
|
@ -315,12 +315,12 @@ export const GraphColoringSelector: {value: ColoringScheme, label: string}[] = [
|
||||||
|
|
||||||
export function getCstStatusColor(status: ExpressionStatus, colors: IColorTheme): string {
|
export function getCstStatusColor(status: ExpressionStatus, colors: IColorTheme): string {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ExpressionStatus.VERIFIED: return colors.green;
|
case ExpressionStatus.VERIFIED: return colors.bgGreen;
|
||||||
case ExpressionStatus.INCORRECT: return colors.red;
|
case ExpressionStatus.INCORRECT: return colors.bgRed;
|
||||||
case ExpressionStatus.INCALCULABLE: return colors.orange;
|
case ExpressionStatus.INCALCULABLE: return colors.bgOrange;
|
||||||
case ExpressionStatus.PROPERTY: return colors.teal;
|
case ExpressionStatus.PROPERTY: return colors.bgTeal;
|
||||||
case ExpressionStatus.UNKNOWN: return colors.blue;
|
case ExpressionStatus.UNKNOWN: return colors.bgBlue;
|
||||||
case ExpressionStatus.UNDEFINED: return colors.blue;
|
case ExpressionStatus.UNDEFINED: return colors.bgBlue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,10 +392,10 @@ export const mapTopicInfo: Map<HelpTopic, IDescriptor> = new Map([
|
||||||
|
|
||||||
export function getCstClassColor(cstClass: CstClass, colors: IColorTheme): string {
|
export function getCstClassColor(cstClass: CstClass, colors: IColorTheme): string {
|
||||||
switch (cstClass) {
|
switch (cstClass) {
|
||||||
case CstClass.BASIC: return colors.green;
|
case CstClass.BASIC: return colors.bgGreen;
|
||||||
case CstClass.DERIVED: return colors.blue;
|
case CstClass.DERIVED: return colors.bgBlue;
|
||||||
case CstClass.STATEMENT: return colors.red;
|
case CstClass.STATEMENT: return colors.bgRed;
|
||||||
case CstClass.TEMPLATE: return colors.teal;
|
case CstClass.TEMPLATE: return colors.bgTeal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -684,7 +684,7 @@ export function getASTNodeColor(node: ISyntaxTreeNode, colors: IColorTheme): str
|
||||||
case TokenID.PUNC_DEFINE:
|
case TokenID.PUNC_DEFINE:
|
||||||
case TokenID.PUNC_STRUCT:
|
case TokenID.PUNC_STRUCT:
|
||||||
case TokenID.ID_LOCAL:
|
case TokenID.ID_LOCAL:
|
||||||
return colors.green;
|
return colors.bgGreen;
|
||||||
|
|
||||||
case TokenID.ID_GLOBAL:
|
case TokenID.ID_GLOBAL:
|
||||||
case TokenID.ID_FUNCTION:
|
case TokenID.ID_FUNCTION:
|
||||||
|
@ -693,7 +693,7 @@ export function getASTNodeColor(node: ISyntaxTreeNode, colors: IColorTheme): str
|
||||||
case TokenID.LIT_INTEGER:
|
case TokenID.LIT_INTEGER:
|
||||||
case TokenID.LIT_EMPTYSET:
|
case TokenID.LIT_EMPTYSET:
|
||||||
case TokenID.LIT_INTSET:
|
case TokenID.LIT_INTSET:
|
||||||
return colors.teal;
|
return colors.bgTeal;
|
||||||
|
|
||||||
case TokenID.FORALL:
|
case TokenID.FORALL:
|
||||||
case TokenID.EXISTS:
|
case TokenID.EXISTS:
|
||||||
|
@ -713,7 +713,7 @@ export function getASTNodeColor(node: ISyntaxTreeNode, colors: IColorTheme): str
|
||||||
case TokenID.SUBSET_OR_EQ:
|
case TokenID.SUBSET_OR_EQ:
|
||||||
case TokenID.SUBSET:
|
case TokenID.SUBSET:
|
||||||
case TokenID.NOTSUBSET:
|
case TokenID.NOTSUBSET:
|
||||||
return colors.orange;
|
return colors.bgOrange;
|
||||||
|
|
||||||
case TokenID.NT_TUPLE:
|
case TokenID.NT_TUPLE:
|
||||||
case TokenID.NT_ENUMERATION:
|
case TokenID.NT_ENUMERATION:
|
||||||
|
@ -733,7 +733,7 @@ export function getASTNodeColor(node: ISyntaxTreeNode, colors: IColorTheme): str
|
||||||
case TokenID.CARD:
|
case TokenID.CARD:
|
||||||
case TokenID.BOOL:
|
case TokenID.BOOL:
|
||||||
case TokenID.DEBOOL:
|
case TokenID.DEBOOL:
|
||||||
return colors.blue;
|
return colors.bgBlue;
|
||||||
|
|
||||||
case TokenID.NT_FUNC_DEFINITION:
|
case TokenID.NT_FUNC_DEFINITION:
|
||||||
case TokenID.NT_DECLARATIVE_EXPR:
|
case TokenID.NT_DECLARATIVE_EXPR:
|
||||||
|
@ -752,8 +752,8 @@ export function getASTNodeColor(node: ISyntaxTreeNode, colors: IColorTheme): str
|
||||||
|
|
||||||
case TokenID.PUNC_ASSIGN:
|
case TokenID.PUNC_ASSIGN:
|
||||||
case TokenID.PUNC_ITERATE:
|
case TokenID.PUNC_ITERATE:
|
||||||
return colors.red;
|
return colors.bgRed;
|
||||||
}
|
}
|
||||||
// node
|
// node
|
||||||
return colors.red;
|
return colors.bgRed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Create venv and install dependencies + imports
|
# Create venv and install dependencies + imports
|
||||||
|
|
||||||
$backend = Resolve-Path -Path "$PSScriptRoot\..\..\rsconcept\backend"
|
$backend = Resolve-Path -Path "$PSScriptRoot\..\..\rsconcept\backend"
|
||||||
$frontend = Resolve-Path -Path "$PSScriptRoot\..\..\rsconcept\fronted"
|
$frontend = Resolve-Path -Path "$PSScriptRoot\..\..\rsconcept\frontend"
|
||||||
$envPath = "$backend\venv"
|
$envPath = "$backend\venv"
|
||||||
$python = "$envPath\Scripts\python.exe"
|
$python = "$envPath\Scripts\python.exe"
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ function LocalDevelopmentSetup() {
|
||||||
|
|
||||||
function FrontendSetup() {
|
function FrontendSetup() {
|
||||||
Set-Location $frontend
|
Set-Location $frontend
|
||||||
& npm install
|
& npm install --only=dev
|
||||||
}
|
}
|
||||||
|
|
||||||
function BackendSetup() {
|
function BackendSetup() {
|
||||||
|
|
|
@ -46,4 +46,7 @@ DjangoDump()
|
||||||
EnsureLocation
|
EnsureLocation
|
||||||
PostgreDump
|
PostgreDump
|
||||||
DjangoDump
|
DjangoDump
|
||||||
echo "Backup created at: $destination"
|
|
||||||
|
green="\033[0;32m"
|
||||||
|
noColor='\033[0m'
|
||||||
|
echo -e "${green}Backup created at: ${destination}${noColor}"
|
|
@ -1,3 +1,4 @@
|
||||||
git pull
|
git pull
|
||||||
|
/bin/bash ./scripts/prod/CreateBackup.sh
|
||||||
docker compose -f "docker-compose-prod.yml" up --build -d
|
docker compose -f "docker-compose-prod.yml" up --build -d
|
||||||
docker image prune -a -f
|
docker image prune -a -f
|
Loading…
Reference in New Issue
Block a user