✨ feat: 初步实现aide decide
This commit is contained in:
313
aide-program/aide/decide/web/app.js
Normal file
313
aide-program/aide/decide/web/app.js
Normal file
@@ -0,0 +1,313 @@
|
||||
const AppState = {
|
||||
task: "",
|
||||
source: "",
|
||||
items: [],
|
||||
decisions: {},
|
||||
notes: {},
|
||||
isSubmitting: false,
|
||||
};
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
const data = await loadItems();
|
||||
AppState.task = data.task || "";
|
||||
AppState.source = data.source || "";
|
||||
AppState.items = Array.isArray(data.items) ? data.items : [];
|
||||
|
||||
renderItems(data);
|
||||
bindEvents();
|
||||
} catch (error) {
|
||||
showError("无法加载待定项数据,请刷新页面重试");
|
||||
}
|
||||
}
|
||||
|
||||
async function loadItems() {
|
||||
const response = await fetch("/api/items");
|
||||
if (!response.ok) {
|
||||
throw new Error("加载失败");
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
function renderItems(data) {
|
||||
const container = document.getElementById("items-container");
|
||||
container.innerHTML = "";
|
||||
|
||||
document.getElementById("task-name").textContent = data.task || "-";
|
||||
document.getElementById("task-source").textContent = data.source || "-";
|
||||
|
||||
data.items.forEach((item) => {
|
||||
container.appendChild(renderItemCard(item));
|
||||
});
|
||||
|
||||
updateProgress();
|
||||
updateSubmitButton();
|
||||
}
|
||||
|
||||
function renderItemCard(item) {
|
||||
const card = document.createElement("article");
|
||||
card.className = "item-card";
|
||||
card.dataset.itemId = String(item.id);
|
||||
|
||||
const header = document.createElement("header");
|
||||
header.className = "item-header";
|
||||
const title = document.createElement("h2");
|
||||
title.className = "item-title";
|
||||
const number = document.createElement("span");
|
||||
number.className = "item-number";
|
||||
number.textContent = `${item.id}.`;
|
||||
const titleText = document.createElement("span");
|
||||
titleText.textContent = item.title || "待定项";
|
||||
title.appendChild(number);
|
||||
title.appendChild(titleText);
|
||||
|
||||
const recommend = document.createElement("span");
|
||||
recommend.className = "recommend-badge";
|
||||
if (item.recommend) {
|
||||
recommend.textContent = `推荐: ${item.recommend}`;
|
||||
} else {
|
||||
recommend.hidden = true;
|
||||
}
|
||||
|
||||
header.appendChild(title);
|
||||
header.appendChild(recommend);
|
||||
card.appendChild(header);
|
||||
|
||||
if (item.context) {
|
||||
const context = document.createElement("div");
|
||||
context.className = "item-context";
|
||||
context.textContent = item.context;
|
||||
card.appendChild(context);
|
||||
}
|
||||
|
||||
if (item.location && item.location.file) {
|
||||
const location = document.createElement("div");
|
||||
location.className = "item-location";
|
||||
location.textContent = `位置: ${item.location.file}:${item.location.start}-${item.location.end}`;
|
||||
card.appendChild(location);
|
||||
}
|
||||
|
||||
const options = renderOptions(item);
|
||||
card.appendChild(options);
|
||||
|
||||
const noteWrap = document.createElement("div");
|
||||
noteWrap.className = "item-note";
|
||||
const noteLabel = document.createElement("label");
|
||||
noteLabel.setAttribute("for", `note-${item.id}`);
|
||||
noteLabel.textContent = "备注(可选):";
|
||||
const textarea = document.createElement("textarea");
|
||||
textarea.id = `note-${item.id}`;
|
||||
textarea.placeholder = "添加补充说明...";
|
||||
noteWrap.appendChild(noteLabel);
|
||||
noteWrap.appendChild(textarea);
|
||||
card.appendChild(noteWrap);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
function renderOptions(item) {
|
||||
const optionsWrap = document.createElement("div");
|
||||
optionsWrap.className = "options-list";
|
||||
const current = AppState.decisions[item.id];
|
||||
|
||||
item.options.forEach((option) => {
|
||||
const label = document.createElement("label");
|
||||
label.className = "option-item";
|
||||
label.dataset.value = option.value;
|
||||
label.dataset.recommended = option.value === item.recommend ? "true" : "false";
|
||||
|
||||
const input = document.createElement("input");
|
||||
input.type = "radio";
|
||||
input.name = `item-${item.id}`;
|
||||
input.value = option.value;
|
||||
if (current === option.value) {
|
||||
input.checked = true;
|
||||
label.classList.add("selected");
|
||||
}
|
||||
|
||||
const content = document.createElement("div");
|
||||
content.className = "option-content";
|
||||
|
||||
const header = document.createElement("div");
|
||||
header.className = "option-header";
|
||||
const optLabel = document.createElement("span");
|
||||
optLabel.className = "option-label";
|
||||
optLabel.textContent = option.label || option.value;
|
||||
header.appendChild(optLabel);
|
||||
|
||||
if (option.score !== undefined && option.score !== null) {
|
||||
const score = document.createElement("span");
|
||||
score.className = "option-score";
|
||||
score.textContent = `评分: ${option.score}`;
|
||||
header.appendChild(score);
|
||||
}
|
||||
|
||||
content.appendChild(header);
|
||||
|
||||
const hasPros = Array.isArray(option.pros) && option.pros.length > 0;
|
||||
const hasCons = Array.isArray(option.cons) && option.cons.length > 0;
|
||||
if (hasPros || hasCons) {
|
||||
const details = document.createElement("div");
|
||||
details.className = "option-details";
|
||||
if (hasPros) {
|
||||
const pros = document.createElement("div");
|
||||
pros.className = "option-pros";
|
||||
pros.innerHTML = `<strong>优点:</strong> ${option.pros.join(",")}`;
|
||||
details.appendChild(pros);
|
||||
}
|
||||
if (hasCons) {
|
||||
const cons = document.createElement("div");
|
||||
cons.className = "option-cons";
|
||||
cons.innerHTML = `<strong>缺点:</strong> ${option.cons.join(",")}`;
|
||||
details.appendChild(cons);
|
||||
}
|
||||
content.appendChild(details);
|
||||
}
|
||||
|
||||
label.appendChild(input);
|
||||
label.appendChild(content);
|
||||
optionsWrap.appendChild(label);
|
||||
});
|
||||
|
||||
return optionsWrap;
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
const container = document.getElementById("items-container");
|
||||
container.addEventListener("change", (event) => {
|
||||
const target = event.target;
|
||||
if (target && target.type === "radio") {
|
||||
const itemId = parseInt(target.name.replace("item-", ""), 10);
|
||||
handleOptionSelect(itemId, target.value);
|
||||
}
|
||||
});
|
||||
|
||||
container.addEventListener("input", (event) => {
|
||||
const target = event.target;
|
||||
if (target && target.tagName === "TEXTAREA") {
|
||||
const itemId = parseInt(target.id.replace("note-", ""), 10);
|
||||
handleNoteInput(itemId, target.value);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("submit-btn").addEventListener("click", submitDecisions);
|
||||
}
|
||||
|
||||
function handleOptionSelect(itemId, value) {
|
||||
AppState.decisions[itemId] = value;
|
||||
const card = document.querySelector(`.item-card[data-item-id="${itemId}"]`);
|
||||
if (card) {
|
||||
const options = card.querySelectorAll(".option-item");
|
||||
options.forEach((opt) => {
|
||||
if (opt.dataset.value === value) {
|
||||
opt.classList.add("selected");
|
||||
} else {
|
||||
opt.classList.remove("selected");
|
||||
}
|
||||
});
|
||||
if (AppState.decisions[itemId]) {
|
||||
card.classList.add("completed");
|
||||
}
|
||||
}
|
||||
updateProgress();
|
||||
updateSubmitButton();
|
||||
}
|
||||
|
||||
function handleNoteInput(itemId, note) {
|
||||
AppState.notes[itemId] = note;
|
||||
}
|
||||
|
||||
function updateProgress() {
|
||||
const total = AppState.items.length;
|
||||
const completed = Object.keys(AppState.decisions).length;
|
||||
const text = document.getElementById("progress-text");
|
||||
text.textContent = `已完成 ${completed}/${total} 项`;
|
||||
}
|
||||
|
||||
function canSubmit() {
|
||||
return (
|
||||
AppState.items.length > 0 &&
|
||||
Object.keys(AppState.decisions).length === AppState.items.length &&
|
||||
!AppState.isSubmitting
|
||||
);
|
||||
}
|
||||
|
||||
function updateSubmitButton() {
|
||||
const button = document.getElementById("submit-btn");
|
||||
const allowed = canSubmit();
|
||||
button.disabled = !allowed;
|
||||
button.setAttribute("aria-disabled", allowed ? "false" : "true");
|
||||
button.textContent = AppState.isSubmitting ? "提交中..." : "提交决策";
|
||||
}
|
||||
|
||||
function buildDecisionData() {
|
||||
const decisions = AppState.items.map((item) => {
|
||||
const note = AppState.notes[item.id];
|
||||
const trimmed = typeof note === "string" ? note.trim() : "";
|
||||
const payload = { id: item.id, chosen: AppState.decisions[item.id] };
|
||||
if (trimmed) {
|
||||
payload.note = trimmed;
|
||||
}
|
||||
return payload;
|
||||
});
|
||||
return { decisions };
|
||||
}
|
||||
|
||||
async function submitDecisions() {
|
||||
if (!canSubmit()) {
|
||||
showError("请先完成所有待定项的选择");
|
||||
return;
|
||||
}
|
||||
AppState.isSubmitting = true;
|
||||
updateSubmitButton();
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/submit", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(buildDecisionData()),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
let detail = "提交失败";
|
||||
try {
|
||||
const error = await response.json();
|
||||
detail = error.detail || detail;
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
throw new Error(detail);
|
||||
}
|
||||
|
||||
showSuccess();
|
||||
} catch (error) {
|
||||
AppState.isSubmitting = false;
|
||||
updateSubmitButton();
|
||||
showError(`提交失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function showSuccess() {
|
||||
const overlay = document.getElementById("success-overlay");
|
||||
overlay.hidden = false;
|
||||
AppState.isSubmitting = false;
|
||||
const button = document.getElementById("submit-btn");
|
||||
button.disabled = true;
|
||||
button.setAttribute("aria-disabled", "true");
|
||||
}
|
||||
|
||||
let errorTimer = null;
|
||||
function showError(message) {
|
||||
const toast = document.getElementById("error-toast");
|
||||
const text = document.getElementById("error-message");
|
||||
text.textContent = message;
|
||||
toast.hidden = false;
|
||||
if (errorTimer) {
|
||||
clearTimeout(errorTimer);
|
||||
}
|
||||
errorTimer = setTimeout(() => {
|
||||
toast.hidden = true;
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
49
aide-program/aide/decide/web/index.html
Normal file
49
aide-program/aide/decide/web/index.html
Normal file
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Aide 待定项确认</title>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<div class="title">
|
||||
<h1>Aide 待定项确认</h1>
|
||||
<p class="subtitle">请根据任务信息完成待定项选择</p>
|
||||
</div>
|
||||
<div class="task-info">
|
||||
<p><strong>任务:</strong> <span id="task-name"></span></p>
|
||||
<p><strong>来源:</strong> <span id="task-source"></span></p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="items-container" id="items-container" aria-label="待定项列表"></main>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="progress">
|
||||
<span id="progress-text">已完成 0/0 项</span>
|
||||
</div>
|
||||
<button class="submit-btn" id="submit-btn" disabled aria-disabled="true" aria-describedby="submit-hint">
|
||||
提交决策
|
||||
</button>
|
||||
<span id="submit-hint" class="sr-only">请先完成所有待定项的选择</span>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="success-overlay" id="success-overlay" hidden>
|
||||
<div class="success-message">
|
||||
<h2>决策已提交</h2>
|
||||
<p>您可以关闭此页面</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="error-toast" id="error-toast" hidden role="alert">
|
||||
<span class="error-icon">✗</span>
|
||||
<span class="error-message" id="error-message"></span>
|
||||
</div>
|
||||
|
||||
<script src="/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
336
aide-program/aide/decide/web/style.css
Normal file
336
aide-program/aide/decide/web/style.css
Normal file
@@ -0,0 +1,336 @@
|
||||
:root {
|
||||
--color-primary: #2563eb;
|
||||
--color-primary-hover: #1d4ed8;
|
||||
--color-success: #16a34a;
|
||||
--color-warning: #ca8a04;
|
||||
--color-error: #dc2626;
|
||||
--color-text: #1f2937;
|
||||
--color-text-secondary: #6b7280;
|
||||
--color-border: #e5e7eb;
|
||||
--color-background: #f9fafb;
|
||||
--color-card: #ffffff;
|
||||
|
||||
--spacing-xs: 4px;
|
||||
--spacing-sm: 8px;
|
||||
--spacing-md: 16px;
|
||||
--spacing-lg: 24px;
|
||||
--spacing-xl: 32px;
|
||||
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
|
||||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
|
||||
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
--font-size-sm: 14px;
|
||||
--font-size-md: 16px;
|
||||
--font-size-lg: 18px;
|
||||
--font-size-xl: 24px;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: var(--font-family);
|
||||
background: radial-gradient(circle at 20% 20%, #f1f5f9, #f9fafb 50%);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: var(--spacing-xl) var(--spacing-lg);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
background: var(--color-card);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-lg);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.title h1 {
|
||||
margin: 0 0 var(--spacing-xs);
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.title .subtitle {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--font-size-md);
|
||||
}
|
||||
|
||||
.task-info p {
|
||||
margin: var(--spacing-xs) 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.items-container {
|
||||
margin: var(--spacing-xl) 0;
|
||||
display: grid;
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.item-card {
|
||||
background: var(--color-card);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.item-card.completed {
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.08);
|
||||
}
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.item-title {
|
||||
margin: 0;
|
||||
font-size: var(--font-size-lg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.item-number {
|
||||
color: var(--color-primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.recommend-badge {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-primary);
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.item-context,
|
||||
.item-location {
|
||||
margin-top: var(--spacing-sm);
|
||||
color: var(--color-text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.options-list {
|
||||
margin-top: var(--spacing-md);
|
||||
display: grid;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.option-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-md);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-md);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s ease, background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.option-item input {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.option-item.selected {
|
||||
border-color: var(--color-primary);
|
||||
background: rgba(37, 99, 235, 0.05);
|
||||
}
|
||||
|
||||
.option-item[data-recommended="true"] {
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
.option-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.option-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.option-score {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.option-details {
|
||||
display: flex;
|
||||
gap: var(--spacing-lg);
|
||||
margin-top: var(--spacing-xs);
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.option-details strong {
|
||||
color: var(--color-text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.item-note {
|
||||
margin-top: var(--spacing-md);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.item-note label {
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.item-note textarea {
|
||||
width: 100%;
|
||||
min-height: 80px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-sm);
|
||||
font-size: var(--font-size-md);
|
||||
resize: vertical;
|
||||
font-family: var(--font-family);
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--spacing-md);
|
||||
background: var(--color-card);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.progress {
|
||||
color: var(--color-text-secondary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-sm) var(--spacing-lg);
|
||||
font-size: var(--font-size-md);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, box-shadow 0.2s ease;
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.submit-btn:hover:not(:disabled) {
|
||||
background: var(--color-primary-hover);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.submit-btn:disabled,
|
||||
.submit-btn[aria-disabled="true"] {
|
||||
background: var(--color-border);
|
||||
color: var(--color-text-secondary);
|
||||
cursor: not-allowed;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.success-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
.success-message {
|
||||
background: var(--color-card);
|
||||
padding: var(--spacing-xl);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-md);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.success-message h2 {
|
||||
margin: 0 0 var(--spacing-sm);
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
.error-toast {
|
||||
position: fixed;
|
||||
bottom: var(--spacing-lg);
|
||||
right: var(--spacing-lg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
background: #fff;
|
||||
border: 1px solid var(--color-error);
|
||||
color: var(--color-error);
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-md);
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.container {
|
||||
padding: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.header {
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.option-details {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.footer {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user