feat: add seatmap context panel and smoke checks
This commit is contained in:
@@ -50,6 +50,7 @@ const seatMapDom = {
|
||||
formGap: null,
|
||||
formImage: document.getElementById("seatmap-admin-form-image"),
|
||||
search: document.getElementById("seatmap-admin-search"),
|
||||
context: document.getElementById("seatmap-admin-context"),
|
||||
unassigned: document.getElementById("seatmap-admin-unassigned"),
|
||||
officeTabs: document.getElementById("seatmap-admin-office-tabs"),
|
||||
sidebarTitle: document.getElementById("seatmap-admin-sidebar-title"),
|
||||
@@ -75,6 +76,7 @@ const seatMapDom = {
|
||||
formGap: null,
|
||||
formImage: null,
|
||||
search: document.getElementById("seatmap-readonly-search"),
|
||||
context: document.getElementById("seatmap-readonly-context"),
|
||||
unassigned: document.getElementById("seatmap-readonly-unassigned"),
|
||||
officeTabs: document.getElementById("seatmap-readonly-office-tabs"),
|
||||
sidebarTitle: document.getElementById("seatmap-readonly-sidebar-title"),
|
||||
@@ -100,6 +102,7 @@ let seatMapFormCols = seatMapDom.admin.formCols;
|
||||
let seatMapFormGap = seatMapDom.admin.formGap;
|
||||
let seatMapFormImage = seatMapDom.admin.formImage;
|
||||
let seatMapSearch = seatMapDom.admin.search;
|
||||
let seatMapContext = seatMapDom.admin.context;
|
||||
let seatMapUnassigned = seatMapDom.admin.unassigned;
|
||||
let seatMapOfficeTabs = seatMapDom.admin.officeTabs;
|
||||
let seatMapSidebarTitle = seatMapDom.admin.sidebarTitle;
|
||||
@@ -135,6 +138,7 @@ const seatMapState = {
|
||||
search: "",
|
||||
status: "",
|
||||
statusTone: "info",
|
||||
selectedMemberId: null,
|
||||
draggingMemberId: null,
|
||||
zoom: 1,
|
||||
panning: false,
|
||||
@@ -491,6 +495,7 @@ function syncSeatMapDomRefs() {
|
||||
seatMapFormGap = dom.formGap;
|
||||
seatMapFormImage = dom.formImage;
|
||||
seatMapSearch = dom.search;
|
||||
seatMapContext = dom.context;
|
||||
seatMapUnassigned = dom.unassigned;
|
||||
seatMapOfficeTabs = dom.officeTabs;
|
||||
seatMapSidebarTitle = dom.sidebarTitle;
|
||||
@@ -837,6 +842,98 @@ function getMemberMap() {
|
||||
return new Map(seatMapState.members.map((member) => [Number(member.id), member]));
|
||||
}
|
||||
|
||||
function buildSeatMapTeamTone(teamName) {
|
||||
const team = String(teamName || "").trim();
|
||||
if (!team) {
|
||||
return {
|
||||
accent: "rgba(120, 132, 119, 0.86)",
|
||||
soft: "rgba(120, 132, 119, 0.18)",
|
||||
label: "미분류",
|
||||
};
|
||||
}
|
||||
const palette = [
|
||||
{ accent: "rgba(13, 148, 136, 0.9)", soft: "rgba(13, 148, 136, 0.18)" },
|
||||
{ accent: "rgba(202, 138, 4, 0.9)", soft: "rgba(202, 138, 4, 0.18)" },
|
||||
{ accent: "rgba(5, 150, 105, 0.9)", soft: "rgba(5, 150, 105, 0.18)" },
|
||||
{ accent: "rgba(180, 83, 9, 0.9)", soft: "rgba(180, 83, 9, 0.18)" },
|
||||
{ accent: "rgba(20, 83, 45, 0.9)", soft: "rgba(20, 83, 45, 0.16)" },
|
||||
{ accent: "rgba(11, 110, 79, 0.9)", soft: "rgba(11, 110, 79, 0.18)" },
|
||||
];
|
||||
let hash = 0;
|
||||
for (const char of team) {
|
||||
hash = ((hash * 31) + char.charCodeAt(0)) % 2147483647;
|
||||
}
|
||||
const picked = palette[Math.abs(hash) % palette.length];
|
||||
return { ...picked, label: team };
|
||||
}
|
||||
|
||||
function getSeatMapTeamStyle(member) {
|
||||
const tone = buildSeatMapTeamTone(getSeatMapOrgUnitLabel(member));
|
||||
return `--seatmap-team-accent:${tone.accent}; --seatmap-team-soft:${tone.soft};`;
|
||||
}
|
||||
|
||||
function renderSeatMapTeamChip(member) {
|
||||
const label = getSeatMapOrgUnitLabel(member);
|
||||
const tone = buildSeatMapTeamTone(label);
|
||||
return `<span class="seatmap-team-chip" style="${escapeHtml(getSeatMapTeamStyle({ team: label }))}">${escapeHtml(label)}</span>`;
|
||||
}
|
||||
|
||||
function getSeatMapOrgPath(member) {
|
||||
const values = [
|
||||
member?.department,
|
||||
member?.grp,
|
||||
member?.division,
|
||||
member?.team,
|
||||
member?.cell,
|
||||
]
|
||||
.map((value) => String(value || "").trim())
|
||||
.filter(Boolean);
|
||||
return values.filter((value, index) => values.indexOf(value) === index);
|
||||
}
|
||||
|
||||
function getSeatMapOrgUnitLabel(member) {
|
||||
return String(
|
||||
member?.team
|
||||
|| member?.division
|
||||
|| member?.grp
|
||||
|| member?.department
|
||||
|| member?.cell
|
||||
|| "미분류"
|
||||
).trim() || "미분류";
|
||||
}
|
||||
|
||||
function getSeatMapOverlayTeamLabel(member) {
|
||||
return String(member?.team || "").trim();
|
||||
}
|
||||
|
||||
function renderSeatMapMemberContext(memberId) {
|
||||
if (!seatMapContext) return;
|
||||
const member = memberId ? getMemberMap().get(Number(memberId)) : null;
|
||||
seatMapState.selectedMemberId = member ? Number(member.id) : null;
|
||||
if (!member) {
|
||||
seatMapContext.classList.add("hidden");
|
||||
seatMapContext.innerHTML = "";
|
||||
return;
|
||||
}
|
||||
const tone = buildSeatMapTeamTone(member.team);
|
||||
const orgPath = getSeatMapOrgPath(member);
|
||||
seatMapContext.classList.remove("hidden");
|
||||
seatMapContext.innerHTML = `
|
||||
<div class="seatmap-context-head">
|
||||
<div class="seatmap-context-title">
|
||||
<strong>${escapeHtml(member.name || "-")}</strong>
|
||||
<span>${escapeHtml(member.rank || member.role || "구성원")}</span>
|
||||
</div>
|
||||
<span class="seatmap-context-badge" style="${escapeHtml(getSeatMapTeamStyle(member))}">${escapeHtml(tone.label)}</span>
|
||||
</div>
|
||||
<div class="seatmap-context-tree">
|
||||
${orgPath.length
|
||||
? orgPath.map((item) => `<span class="seatmap-context-node">${escapeHtml(item)}</span>`).join("")
|
||||
: '<div class="seatmap-context-empty">표시할 상위 조직 정보가 없습니다.</div>'}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function getPlacementForMember(memberId) {
|
||||
return getPlacementSource().find((item) => Number(item.member_id) === Number(memberId)) || null;
|
||||
}
|
||||
@@ -871,9 +968,13 @@ function getSidebarMembers() {
|
||||
return members.filter(memberMatchesSeatMapSearch);
|
||||
}
|
||||
|
||||
function focusSeatMapMember(memberId) {
|
||||
function focusSeatMapMember(memberId, options = {}) {
|
||||
const showContext = options.showContext !== false;
|
||||
const placement = getPlacementForMember(memberId);
|
||||
const member = getMemberMap().get(Number(memberId));
|
||||
if (showContext) {
|
||||
renderSeatMapMemberContext(memberId);
|
||||
}
|
||||
if (!placement) {
|
||||
setSeatMapStatus("해당 인원은 아직 배치되지 않았습니다.", "info");
|
||||
return;
|
||||
@@ -943,6 +1044,46 @@ function getSlotPlacementMap() {
|
||||
return slotMap;
|
||||
}
|
||||
|
||||
function buildSeatMapGridTeamOverlays(rows, cols, placementMap, memberMap) {
|
||||
const groups = new Map();
|
||||
placementMap.forEach((placement) => {
|
||||
const member = memberMap.get(Number(placement.member_id));
|
||||
if (!member) return;
|
||||
const label = getSeatMapOverlayTeamLabel(member);
|
||||
if (!label) return;
|
||||
const rowIndex = Number(placement.row_index);
|
||||
const colIndex = Number(placement.col_index);
|
||||
if (!Number.isFinite(rowIndex) || !Number.isFinite(colIndex)) return;
|
||||
if (!groups.has(label)) {
|
||||
groups.set(label, {
|
||||
label,
|
||||
toneMember: { team: label },
|
||||
minRow: rowIndex,
|
||||
maxRow: rowIndex,
|
||||
minCol: colIndex,
|
||||
maxCol: colIndex,
|
||||
count: 1,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const group = groups.get(label);
|
||||
group.minRow = Math.min(group.minRow, rowIndex);
|
||||
group.maxRow = Math.max(group.maxRow, rowIndex);
|
||||
group.minCol = Math.min(group.minCol, colIndex);
|
||||
group.maxCol = Math.max(group.maxCol, colIndex);
|
||||
group.count += 1;
|
||||
});
|
||||
return Array.from(groups.values())
|
||||
.filter((group) => group.count >= 2 && group.maxRow < rows && group.maxCol < cols)
|
||||
.map((group) => `
|
||||
<div
|
||||
class="seatmap-team-overlay"
|
||||
data-team-label="${escapeHtml(group.label)}"
|
||||
style="${escapeHtml(getSeatMapTeamStyle(group.toneMember))}; grid-column:${group.minCol + 1} / ${group.maxCol + 2}; grid-row:${group.minRow + 1} / ${group.maxRow + 2};"
|
||||
></div>
|
||||
`);
|
||||
}
|
||||
|
||||
function upsertDraftPlacement(memberId, rowIndex, colIndex) {
|
||||
const cellMap = getCellPlacementMap();
|
||||
const existing = cellMap.get(`${rowIndex}:${colIndex}`);
|
||||
@@ -1003,11 +1144,12 @@ function renderMemberCard(member, draggable) {
|
||||
? `<span class="seatmap-member-avatar"><img src="${photoUrl}" alt="${escapeHtml(member.name)}"></span>`
|
||||
: `<span class="seatmap-member-avatar seatmap-member-avatar-fallback">${escapeHtml(getInitials(member.name))}</span>`;
|
||||
return `
|
||||
<div class="seatmap-member-card${draggable ? " draggable" : ""}" draggable="${draggable}" data-member-id="${Number(member.id)}">
|
||||
<div class="seatmap-member-card team-colored${draggable ? " draggable" : ""}" style="${escapeHtml(getSeatMapTeamStyle(member))}" draggable="${draggable}" data-member-id="${Number(member.id)}">
|
||||
${avatar}
|
||||
<span class="seatmap-member-text">
|
||||
<strong>${escapeHtml(member.name || "-")}</strong>
|
||||
<em>${escapeHtml(member.department || member.team || member.rank || "-")}</em>
|
||||
<em>${escapeHtml(member.rank || "-")}</em>
|
||||
<span class="seatmap-team-chip" style="${escapeHtml(getSeatMapTeamStyle({ team: getSeatMapOrgUnitLabel(member) }))}">${escapeHtml(getSeatMapOrgUnitLabel(member))}</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
@@ -1015,11 +1157,12 @@ function renderMemberCard(member, draggable) {
|
||||
|
||||
function renderUnassignedMemberCard(member, draggable) {
|
||||
return `
|
||||
<div class="seatmap-member-card seatmap-member-card-compact${draggable ? " draggable" : ""}" draggable="${draggable}" data-member-id="${Number(member.id)}">
|
||||
<div class="seatmap-member-card seatmap-member-card-compact${draggable ? " draggable" : ""}" style="${escapeHtml(getSeatMapTeamStyle(member))}" draggable="${draggable}" data-member-id="${Number(member.id)}">
|
||||
<span class="seatmap-member-text seatmap-member-text-inline">
|
||||
<strong>${escapeHtml(member.name || "-")}</strong>
|
||||
<em>${escapeHtml(member.rank || "-")}</em>
|
||||
</span>
|
||||
${renderSeatMapTeamChip(member)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -1027,14 +1170,13 @@ function renderUnassignedMemberCard(member, draggable) {
|
||||
function renderSeatMapSearchCard(member) {
|
||||
const placement = getPlacementForMember(Number(member.id));
|
||||
if (!placement) return "";
|
||||
const badge = `<span class="seatmap-member-badge occupied">${escapeHtml(placement.seat_label || "배치완료")}</span>`;
|
||||
return `
|
||||
<button class="seatmap-member-search-card" type="button" data-member-id="${Number(member.id)}">
|
||||
<span class="seatmap-member-text seatmap-member-text-inline">
|
||||
<strong>${escapeHtml(member.name || "-")}</strong>
|
||||
<em>${escapeHtml(member.rank || member.department || "-")}</em>
|
||||
<em>${escapeHtml(member.rank || "-")}</em>
|
||||
</span>
|
||||
${badge}
|
||||
${renderSeatMapTeamChip(member)}
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
@@ -1054,14 +1196,16 @@ function renderSeatMapBoard() {
|
||||
const gap = Number(seatMapState.seatMap.cell_gap || 0);
|
||||
const editable = seatMapState.editMode && isAdmin();
|
||||
const cells = [];
|
||||
const overlays = buildSeatMapGridTeamOverlays(rows, cols, placementMap, memberMap);
|
||||
|
||||
for (let rowIndex = 0; rowIndex < rows; rowIndex += 1) {
|
||||
for (let colIndex = 0; colIndex < cols; colIndex += 1) {
|
||||
const key = `${rowIndex}:${colIndex}`;
|
||||
const placement = placementMap.get(key);
|
||||
const member = placement ? memberMap.get(Number(placement.member_id)) : null;
|
||||
const teamStyle = member ? ` style="${escapeHtml(getSeatMapTeamStyle(member))}"` : "";
|
||||
cells.push(`
|
||||
<div class="seatmap-cell${placement ? " occupied" : ""}${editable ? " editable" : ""}" data-row="${rowIndex}" data-col="${colIndex}">
|
||||
<div class="seatmap-cell${placement ? " occupied" : ""}${member ? " team-colored" : ""}${editable ? " editable" : ""}" data-row="${rowIndex}" data-col="${colIndex}"${teamStyle}>
|
||||
<span class="seatmap-cell-label">${escapeHtml(computeSeatLabel(rowIndex, colIndex))}</span>
|
||||
${member ? renderMemberCard(member, editable) : ""}
|
||||
</div>
|
||||
@@ -1072,7 +1216,7 @@ function renderSeatMapBoard() {
|
||||
seatMapBoard.innerHTML = `
|
||||
<div class="seatmap-canvas" style="--seatmap-rows:${rows}; --seatmap-cols:${cols}; --seatmap-gap:${gap}px;">
|
||||
<img class="seatmap-image" src="${escapeHtml(seatMapState.seatMap.image_url)}" alt="${escapeHtml(seatMapState.seatMap.name)}">
|
||||
<div class="seatmap-grid">${cells.join("")}</div>
|
||||
<div class="seatmap-grid">${overlays.join("")}${cells.join("")}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -1175,6 +1319,7 @@ function renderSeatMapActions() {
|
||||
function updateSeatMapDraftUi() {
|
||||
renderSeatMapActions();
|
||||
renderUnassignedMembers();
|
||||
renderSeatMapMemberContext(seatMapState.selectedMemberId);
|
||||
syncSeatMapViewerFrame();
|
||||
}
|
||||
|
||||
@@ -1394,6 +1539,9 @@ function handleEmbeddedNavigationMessage(event) {
|
||||
updateSeatMapDraftUi();
|
||||
}
|
||||
}
|
||||
if (data.type === "seatmap-member-selected") {
|
||||
renderSeatMapMemberContext(Number(data.memberId || 0) || null);
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchJson(url, options) {
|
||||
@@ -1437,6 +1585,7 @@ async function loadSeatMapData(force = false) {
|
||||
seatMapState.placements = clonePlacements(layoutPayload.placements || []);
|
||||
seatMapState.zoom = 1;
|
||||
seatMapState.hoveredSlotId = null;
|
||||
seatMapState.selectedMemberId = null;
|
||||
seatMapState.editMode = canEditSeatMap();
|
||||
resetSeatMapDraft();
|
||||
seatMapState.loaded = true;
|
||||
@@ -1450,6 +1599,7 @@ async function loadSeatMapData(force = false) {
|
||||
seatMapState.placements = [];
|
||||
seatMapState.zoom = 1;
|
||||
seatMapState.hoveredSlotId = null;
|
||||
seatMapState.selectedMemberId = null;
|
||||
seatMapState.editMode = canEditSeatMap();
|
||||
resetSeatMapDraft();
|
||||
seatMapState.loaded = true;
|
||||
@@ -1690,7 +1840,7 @@ function setActiveView(view) {
|
||||
dbStatusFrame.src = resolveAppUrl(frameSrc);
|
||||
}
|
||||
if (isSeatMapAdmin || isSeatMapReadonly) {
|
||||
loadSeatMapData();
|
||||
loadSeatMapData(previousView !== currentView);
|
||||
}
|
||||
notifyEmbeddedTabActivated();
|
||||
}
|
||||
@@ -1886,14 +2036,15 @@ Object.values(seatMapDom).forEach((dom) => {
|
||||
});
|
||||
|
||||
dom.unassigned?.addEventListener("click", (event) => {
|
||||
const button = event.target.closest("[data-member-id]");
|
||||
if (!button) return;
|
||||
if (button.classList.contains("seatmap-member-search-card")) {
|
||||
focusSeatMapMember(Number(button.dataset.memberId));
|
||||
const target = event.target.closest("[data-member-id]");
|
||||
if (!target) return;
|
||||
if (target.classList.contains("seatmap-member-search-card")) {
|
||||
focusSeatMapMember(Number(target.dataset.memberId), { showContext: false });
|
||||
return;
|
||||
}
|
||||
renderSeatMapMemberContext(Number(target.dataset.memberId));
|
||||
if (canEditSeatMap()) return;
|
||||
focusSeatMapMember(Number(button.dataset.memberId));
|
||||
focusSeatMapMember(Number(target.dataset.memberId));
|
||||
});
|
||||
dom.unassigned?.addEventListener("dragover", (event) => {
|
||||
if (!seatMapState.editMode) return;
|
||||
@@ -1922,6 +2073,17 @@ Object.values(seatMapDom).forEach((dom) => {
|
||||
if (!fitButton) return;
|
||||
fitDxfSeatMapBoard();
|
||||
});
|
||||
dom.board?.addEventListener("click", (event) => {
|
||||
const memberCard = event.target.closest(".seatmap-member-card[data-member-id]");
|
||||
if (memberCard) {
|
||||
renderSeatMapMemberContext(Number(memberCard.dataset.memberId));
|
||||
return;
|
||||
}
|
||||
const cell = event.target.closest(".seatmap-cell[data-row][data-col]");
|
||||
if (!cell) return;
|
||||
const placement = getCellPlacementMap().get(`${Number(cell.dataset.row)}:${Number(cell.dataset.col)}`);
|
||||
renderSeatMapMemberContext(placement ? Number(placement.member_id) : null);
|
||||
});
|
||||
dom.board?.addEventListener("dragover", (event) => {
|
||||
if (!seatMapState.editMode) return;
|
||||
const target = isSlotBasedSeatMap()
|
||||
|
||||
@@ -181,6 +181,7 @@
|
||||
<span class="hidden">구성원 검색</span>
|
||||
<input id="seatmap-admin-search" type="search" placeholder="이름 또는 부서 검색">
|
||||
</label>
|
||||
<div id="seatmap-admin-context" class="seatmap-context-panel hidden"></div>
|
||||
<div id="seatmap-admin-unassigned" class="seatmap-member-list"></div>
|
||||
</section>
|
||||
</aside>
|
||||
@@ -220,6 +221,7 @@
|
||||
<span class="hidden">구성원 검색</span>
|
||||
<input id="seatmap-readonly-search" type="search" placeholder="이름 또는 부서 검색">
|
||||
</label>
|
||||
<div id="seatmap-readonly-context" class="seatmap-context-panel hidden"></div>
|
||||
<div id="seatmap-readonly-unassigned" class="seatmap-member-list"></div>
|
||||
</section>
|
||||
</aside>
|
||||
|
||||
@@ -913,6 +913,39 @@ body {
|
||||
padding: var(--seatmap-gap);
|
||||
}
|
||||
|
||||
.seatmap-team-overlay {
|
||||
pointer-events: none;
|
||||
align-self: stretch;
|
||||
justify-self: stretch;
|
||||
border-radius: 20px;
|
||||
border: 2px solid color-mix(in srgb, var(--seatmap-team-accent, rgba(13, 148, 136, 0.3)) 62%, white);
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
color-mix(in srgb, var(--seatmap-team-soft, rgba(13, 148, 136, 0.12)) 100%, transparent),
|
||||
color-mix(in srgb, var(--seatmap-team-soft, rgba(13, 148, 136, 0.08)) 90%, transparent)
|
||||
);
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22), 0 8px 18px rgba(16, 37, 29, 0.08);
|
||||
margin: 4px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.seatmap-team-overlay::before {
|
||||
content: attr(data-team-label);
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 26px;
|
||||
padding: 0 11px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--seatmap-team-soft, rgba(13, 148, 136, 0.14)) 84%, white);
|
||||
color: color-mix(in srgb, var(--seatmap-team-accent, #0f766e) 86%, #10251d);
|
||||
font-size: 11px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.seatmap-cell {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
@@ -920,6 +953,7 @@ body {
|
||||
border: 1px dashed rgba(15, 23, 42, 0.14);
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
transition: border-color 0.18s ease, background 0.18s ease;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.seatmap-cell.editable:hover {
|
||||
@@ -931,6 +965,13 @@ body {
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.seatmap-cell.occupied.team-colored {
|
||||
border-color: color-mix(in srgb, var(--seatmap-team-accent, rgba(15, 118, 110, 0.72)) 62%, white);
|
||||
background:
|
||||
linear-gradient(180deg, color-mix(in srgb, var(--seatmap-team-soft, rgba(15, 118, 110, 0.14)) 86%, transparent), rgba(255, 255, 255, 0.14)),
|
||||
rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.seatmap-cell-label {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
@@ -959,6 +1000,12 @@ body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.seatmap-member-card.team-colored {
|
||||
border-color: color-mix(in srgb, var(--seatmap-team-accent, rgba(245, 158, 11, 0.95)) 42%, rgba(255,255,255,0.4));
|
||||
background:
|
||||
linear-gradient(180deg, color-mix(in srgb, var(--seatmap-team-soft, rgba(245, 158, 11, 0.18)) 68%, rgba(15, 23, 42, 0.84)) 0%, rgba(15, 23, 42, 0.84) 100%);
|
||||
}
|
||||
|
||||
.seatmap-member-card.draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
@@ -1008,11 +1055,26 @@ body {
|
||||
}
|
||||
|
||||
.seatmap-member-text em {
|
||||
color: rgba(226, 232, 240, 0.84);
|
||||
color: rgba(255, 247, 237, 0.96);
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.seatmap-team-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
max-width: 100%;
|
||||
padding: 2px 6px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--seatmap-team-soft, rgba(245, 158, 11, 0.18)) 88%, white);
|
||||
color: color-mix(in srgb, var(--seatmap-team-accent, #b45309) 85%, #10251d);
|
||||
font-size: 10px;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.seatmap-sidebar {
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
@@ -1195,6 +1257,11 @@ body {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.seatmap-member-search-card.team-colored {
|
||||
border-color: color-mix(in srgb, var(--seatmap-team-accent, rgba(245, 158, 11, 0.24)) 34%, rgba(226, 232, 240, 0.9));
|
||||
background: linear-gradient(180deg, color-mix(in srgb, var(--seatmap-team-soft, rgba(245, 158, 11, 0.1)) 78%, white), #fff);
|
||||
}
|
||||
|
||||
.seatmap-member-badge {
|
||||
flex: 0 0 auto;
|
||||
display: inline-flex;
|
||||
@@ -1219,8 +1286,8 @@ body {
|
||||
min-height: 42px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 12px;
|
||||
background: #3f4658;
|
||||
border: 1px solid rgba(148, 163, 184, 0.14);
|
||||
background: transparent;
|
||||
border: 1px solid rgba(194, 170, 134, 0.34);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@@ -1232,12 +1299,14 @@ body {
|
||||
|
||||
.seatmap-member-text-inline strong {
|
||||
font-size: 13px;
|
||||
color: #10251d;
|
||||
}
|
||||
|
||||
.seatmap-member-text-inline em {
|
||||
display: inline;
|
||||
color: rgba(226, 232, 240, 0.8);
|
||||
color: #6f5b3e;
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.seatmap-slot .seatmap-member-card {
|
||||
@@ -1266,6 +1335,81 @@ body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.seatmap-context-panel {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
padding: 14px;
|
||||
border: 1px solid rgba(194, 170, 134, 0.28);
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(180deg, rgba(255, 252, 247, 0.96), rgba(246, 239, 227, 0.92));
|
||||
box-shadow: 0 14px 28px rgba(16, 37, 29, 0.08);
|
||||
}
|
||||
|
||||
.seatmap-context-panel.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.seatmap-context-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.seatmap-context-title {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.seatmap-context-title strong {
|
||||
color: #10251d;
|
||||
font-size: 14px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.seatmap-context-title span {
|
||||
color: #6b7280;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.seatmap-context-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 28px;
|
||||
padding: 0 10px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--seatmap-team-soft, rgba(245, 158, 11, 0.14)) 88%, white);
|
||||
color: color-mix(in srgb, var(--seatmap-team-accent, #b45309) 84%, #10251d);
|
||||
font-size: 11px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.seatmap-context-tree {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.seatmap-context-node {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
padding: 0 11px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.84);
|
||||
border: 1px solid rgba(194, 170, 134, 0.28);
|
||||
color: #30423a;
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.seatmap-context-empty {
|
||||
color: #7b7b6c;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.seatmap-dxf-stage {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user