fix: custom select dropdown, modal styling, memo label wrap, modal button padding
All checks were successful
Deploy to Production / deploy (push) Successful in 1m5s

This commit is contained in:
Johngreen
2026-02-10 16:28:10 +09:00
parent 00a17c0b86
commit c67f114cde
3 changed files with 156 additions and 17 deletions

View File

@@ -7,6 +7,67 @@ import { useToast } from '@/lib/toast-context';
import { api } from '@/lib/api';
import type { InspectionItemDetail } from '@/lib/types';
function CustomSelect({
value,
options,
onChange,
disabled,
placeholder,
}: {
value: string;
options: string[];
onChange: (val: string) => void;
disabled?: boolean;
placeholder?: string;
}) {
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!open) return;
const handler = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
};
document.addEventListener('mousedown', handler);
return () => document.removeEventListener('mousedown', handler);
}, [open]);
const displayLabel = value || placeholder || '선택하세요';
return (
<div className="custom-select" ref={ref}>
<button
type="button"
className={`custom-select-trigger${disabled ? ' disabled' : ''}`}
onClick={() => !disabled && setOpen(!open)}
disabled={disabled}
>
<span className={value ? '' : 'placeholder'}>{displayLabel}</span>
<span className="material-symbols-outlined custom-select-arrow">expand_more</span>
</button>
{open && (
<ul className="custom-select-dropdown">
<li
className={`custom-select-option${!value ? ' selected' : ''}`}
onClick={() => { onChange(''); setOpen(false); }}
>
{placeholder || '선택하세요'}
</li>
{options.map((opt) => (
<li
key={opt}
className={`custom-select-option${value === opt ? ' selected' : ''}`}
onClick={() => { onChange(opt); setOpen(false); }}
>
{opt}
</li>
))}
</ul>
)}
</div>
);
}
interface LocalValues {
[templateItemId: string]: {
value_numeric?: number | null;
@@ -314,17 +375,13 @@ export default function InspectionDetailPage() {
{item.data_type === 'select' && (
<div className="inspection-input-row">
<select
className="form-select"
<CustomSelect
value={local.value_select || ''}
onChange={(e) => handleSelectChange(record.template_item_id, e.target.value)}
options={item.select_options || []}
onChange={(val) => handleSelectChange(record.template_item_id, val)}
disabled={!isEditable}
>
<option value=""></option>
{(item.select_options || []).map((opt) => (
<option key={opt} value={opt}>{opt}</option>
))}
</select>
placeholder="선택하세요"
/>
</div>
)}
</div>
@@ -335,19 +392,19 @@ export default function InspectionDetailPage() {
{showCompleteModal && (
<div className="modal-overlay" onClick={() => setShowCompleteModal(false)}>
<div className="modal" onClick={(e) => e.stopPropagation()}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<div className="modal-header">
<h3> </h3>
<button className="btn-icon" onClick={() => setShowCompleteModal(false)}>
<span className="material-symbols-outlined">close</span>
</button>
</div>
<div className="modal-body">
<div className="modal-body-form">
<p className="complete-summary">
{filledCount}/{totalCount}
</p>
<div className="form-row">
<label> ()</label>
<div className="form-field">
<label className="form-label"> ()</label>
<textarea
className="form-textarea"
value={completionNotes}
@@ -357,7 +414,7 @@ export default function InspectionDetailPage() {
/>
</div>
</div>
<div className="modal-footer">
<div className="modal-actions">
<button className="btn-outline" onClick={() => setShowCompleteModal(false)}>
</button>

View File

@@ -169,14 +169,14 @@ export default function InspectionsPage() {
{showNewModal && (
<div className="modal-overlay" onClick={() => setShowNewModal(false)}>
<div className="modal" onClick={(e) => e.stopPropagation()}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<div className="modal-header">
<h3> </h3>
<button className="btn-icon" onClick={() => setShowNewModal(false)}>
<span className="material-symbols-outlined">close</span>
</button>
</div>
<div className="modal-body">
<div className="modal-body-form">
{activeTemplates.length === 0 ? (
<div className="empty-state">
<p> 릿 .</p>

View File

@@ -979,7 +979,7 @@ a {
align-items: center;
justify-content: flex-end;
gap: 8px;
padding-top: 8px;
padding: 16px 24px 20px;
}
.modal-body-form {
@@ -1645,6 +1645,88 @@ a {
cursor: not-allowed;
}
/* ===== Custom Select (inspection form) ===== */
.custom-select {
position: relative;
min-width: 200px;
}
.custom-select-trigger {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 10px 14px;
border: 1px solid var(--md-outline-variant);
border-radius: 8px;
font-size: 14px;
background: var(--md-surface);
cursor: pointer;
text-align: left;
gap: 8px;
transition: border-color 0.15s;
}
.custom-select-trigger:hover:not(.disabled) {
border-color: var(--md-primary);
}
.custom-select-trigger:focus {
outline: none;
border-color: var(--md-primary);
}
.custom-select-trigger.disabled {
background: var(--md-surface-variant);
cursor: not-allowed;
color: var(--md-on-surface-variant);
}
.custom-select-trigger .placeholder {
color: var(--md-on-surface-variant);
}
.custom-select-arrow {
font-size: 20px;
color: var(--md-on-surface-variant);
flex-shrink: 0;
}
.custom-select-dropdown {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
background: var(--md-surface);
border: 1px solid var(--md-outline-variant);
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
z-index: 100;
list-style: none;
padding: 4px 0;
margin: 0;
max-height: 240px;
overflow-y: auto;
}
.custom-select-option {
padding: 10px 14px;
font-size: 14px;
cursor: pointer;
transition: background 0.1s;
}
.custom-select-option:hover {
background: var(--md-surface-variant);
}
.custom-select-option.selected {
color: var(--md-primary);
font-weight: 500;
background: rgba(26, 115, 232, 0.06);
}
.complete-summary {
font-size: 16px;
font-weight: 500;