feat: request portal — bespoke forms + approval mechanism
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
@import '../../../../styles/design-tokens';
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
|
||||
gap: 14px;
|
||||
margin-top: $sp-6;
|
||||
}
|
||||
|
||||
.tile {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
padding: 22px 24px;
|
||||
text-align: left;
|
||||
background: $white;
|
||||
border: 1px solid $gray-200;
|
||||
border-radius: $r-md;
|
||||
box-shadow: $sh-1;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
font-family: $font-sans;
|
||||
transition: box-shadow .22s, transform .22s, border-color .22s;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 3px;
|
||||
background: $grad-gold-v;
|
||||
opacity: 0;
|
||||
transition: opacity .22s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: $sh-3;
|
||||
transform: translateY(-2px);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.tileIcon {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: $r-md;
|
||||
background: $navy-50;
|
||||
border: 1px solid $navy-100;
|
||||
color: $navy-700;
|
||||
transition: background .22s, color .22s, border-color .22s;
|
||||
|
||||
.tile:hover & {
|
||||
background: $gold-50;
|
||||
border-color: $gold-300;
|
||||
color: $gold-600;
|
||||
}
|
||||
}
|
||||
|
||||
.tileBody {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.tileTitle {
|
||||
font-size: 14.5px;
|
||||
font-weight: 600;
|
||||
letter-spacing: .1px;
|
||||
color: $navy-900;
|
||||
}
|
||||
|
||||
.tileDesc {
|
||||
font-size: 12px;
|
||||
color: $gray-400;
|
||||
}
|
||||
|
||||
.tileArrow {
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
color: $gray-300;
|
||||
transition: color .22s, transform .22s;
|
||||
|
||||
.tile:hover & {
|
||||
color: $gold-500;
|
||||
transform: translateX(3px);
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
margin-top: $sp-6;
|
||||
color: $gray-500;
|
||||
font-size: $fs-sm;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import * as React from 'react';
|
||||
import styles from './RequestGrid.module.scss';
|
||||
import { Icon } from '../../shared/ui/Icon/Icon';
|
||||
import { IRequestType } from '../../models/types';
|
||||
|
||||
interface IRequestGridProps {
|
||||
requests: IRequestType[];
|
||||
onSelect: (request: IRequestType) => void;
|
||||
}
|
||||
|
||||
export const RequestGrid: React.FC<IRequestGridProps> = ({ requests, onSelect }) => {
|
||||
if (requests.length === 0) {
|
||||
return <p className={styles.empty}>В этой категории пока нет заявок.</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.grid}>
|
||||
{requests.map((request) => (
|
||||
<button
|
||||
key={request.id}
|
||||
type="button"
|
||||
className={styles.tile}
|
||||
onClick={() => onSelect(request)}
|
||||
>
|
||||
<span className={styles.tileIcon}>
|
||||
<Icon name={request.icon} />
|
||||
</span>
|
||||
<span className={styles.tileBody}>
|
||||
<span className={styles.tileTitle}>{request.title}</span>
|
||||
{request.description && <span className={styles.tileDesc}>{request.description}</span>}
|
||||
</span>
|
||||
<span className={styles.tileArrow}>
|
||||
<Icon name="chevron-right" size="sm" />
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user