feat: port stroy-bot-project-assigner as resources page
Add the "resources" section (?page=resources&projectId=): professions and companies assigned to a project, with the same lists, fields, texts and optimistic add/remove with rollback as the legacy web part. PnPjs is replaced by SharePoint REST through the app's IHttpClient so the bundle needs no new dependency: the same $select/$expand/$filter, PnP paging replaced by following odata.nextLink, adds as POST to items with the same body, deletes as POST with X-HTTP-Method DELETE and IF-MATCH *. Known legacy edge cases (temporary negative id) are kept and listed in the checklist. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
@import '../../../../styles/design-tokens';
|
||||
|
||||
// Длинный справочник прокручивается внутри колонки — как в assigner.
|
||||
$pool-max-height: 440px;
|
||||
// Минимальная высота строки — чтобы строки с кнопкой и без неё были одной высоты.
|
||||
$item-row-min-height: 40px;
|
||||
// Вертикальный отступ строки списка — как в assigner (между $sp-2 и $sp-3).
|
||||
$item-row-padding-y: 10px;
|
||||
|
||||
.page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $sp-4;
|
||||
width: 100%;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.projectHeader {
|
||||
box-shadow: $sh-2;
|
||||
@include page-header;
|
||||
}
|
||||
|
||||
.projectTitle {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin: 0;
|
||||
font-family: $font-serif;
|
||||
font-size: $fs-page-title;
|
||||
font-weight: 600;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.dismissibleAlert {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: $sp-2;
|
||||
}
|
||||
|
||||
.dismissibleAlertBody {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.tabContent {
|
||||
padding-top: $sp-1;
|
||||
}
|
||||
|
||||
.splitColumns {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.splitLeft {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-right: $sp-5;
|
||||
border-right: 1px solid $gray-200;
|
||||
}
|
||||
|
||||
.splitRight {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-left: $sp-5;
|
||||
}
|
||||
|
||||
.panelColumn {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.panelTitle {
|
||||
position: relative;
|
||||
margin: 0 0 $sp-3;
|
||||
padding-bottom: $sp-2;
|
||||
font-size: $fs-overline;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1.6px;
|
||||
text-transform: uppercase;
|
||||
color: $gray-400;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
@include hairline;
|
||||
}
|
||||
}
|
||||
|
||||
.searchBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $sp-1;
|
||||
margin-bottom: $sp-2;
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.poolScrollArea {
|
||||
max-height: $pool-max-height;
|
||||
overflow-y: auto;
|
||||
border: 1px solid $gray-200;
|
||||
border-radius: $r-sm;
|
||||
background: $white;
|
||||
}
|
||||
|
||||
.itemRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: $sp-2;
|
||||
min-height: $item-row-min-height;
|
||||
padding: $item-row-padding-y $sp-3;
|
||||
border-bottom: 1px solid $gray-100;
|
||||
transition: background 0.12s;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $gold-50;
|
||||
}
|
||||
}
|
||||
|
||||
.itemName,
|
||||
.itemNameMuted {
|
||||
flex: 1;
|
||||
font-size: $fs-body;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.itemName {
|
||||
font-weight: 500;
|
||||
color: $gray-700;
|
||||
}
|
||||
|
||||
.itemNameMuted {
|
||||
color: $gray-400;
|
||||
}
|
||||
|
||||
.alreadyAddedTag {
|
||||
margin-left: $sp-2;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.emptyMsg {
|
||||
padding: $sp-4 $sp-3;
|
||||
font-size: $fs-sm;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
color: $gray-400;
|
||||
}
|
||||
|
||||
@include below($bp-md) {
|
||||
.splitColumns {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.splitLeft {
|
||||
width: 100%;
|
||||
padding-right: 0;
|
||||
padding-bottom: $sp-4;
|
||||
margin-bottom: $sp-4;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid $gray-200;
|
||||
}
|
||||
|
||||
.splitRight {
|
||||
width: 100%;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user