feat(editor, settings): refactor state handling and enhance validation

Refactored editor.page.html to use a Vue-style `data()` function for reactive state, adding a new `content` property and cleaning up redundant inline styles. Updated profile-form.html to replace single `error` handling with field-specific `errors.<field>` bindings, improving form validation clarity and user feedback.
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-11-15 21:52:53 -03:00
parent f2624aef94
commit 02eaac783f
21 changed files with 391 additions and 365 deletions

View file

@ -186,16 +186,22 @@ import './style.css';
export default {
// Reactive state
fileName: 'Document 1',
fontSize: '12',
fontFamily: 'Calibri',
textColor: '#000000',
highlightColor: '#ffff00',
activeTab: 'home',
zoom: 100,
pages: [1],
editor: null,
fileInputRef: null,
data() {
return {
fileName: 'Document 1',
fontSize: '12',
fontFamily: 'Calibri',
textColor: '#000000',
highlightColor: '#ffff00',
activeTab: 'home',
zoom: 100,
pages: [1],
editor: null,
fileInputRef: null,
content: '',
activeTab: 'home'
}
},
// Lifecycle
async mounted() {
@ -334,7 +340,3 @@ export default {
},
};
</script>
<style>
/* Original styles moved to global CSS */
</style>

View file

@ -0,0 +1,39 @@
<!-- Basic player page -->
<template>
<div class="player-container">
<h1>Player</h1>
<div class="player-content">
<p>Player functionality will be implemented here</p>
</div>
</div>
</template>
<script>
import './style.css';
export default {
data() {
return {
currentTrack: null,
isPlaying: false,
volume: 80,
currentTime: 0,
duration: 0,
playlist: [],
shuffle: false,
repeat: false
}
},
methods: {
play() {
this.isPlaying = true;
},
pause() {
this.isPlaying = false;
},
setVolume(vol) {
this.volume = Math.max(0, Math.min(100, vol));
}
}
};
</script>

View file

@ -4,10 +4,10 @@
<controller name="username">
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Username</label>
<input class="w-full p-2 border rounded {error ? 'border-red-500' : 'border-gray-300'}"
<input class="w-full p-2 border rounded {errors.username ? 'border-red-500' : 'border-gray-300'}"
bind="{username}"
placeholder="Enter username" />
{error && <p class="text-red-500 text-xs mt-1">{error.message}</p>}
{errors.username && <p class="text-red-500 text-xs mt-1">{errors.username.message}</p>}
<p class="text-sm text-gray-500 mt-1">
This is your public display name. It can be your real name or a pseudonym. You can only change this once every 30 days.
</p>
@ -18,10 +18,10 @@
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Email</label>
<input type="email"
class="w-full p-2 border rounded {error ? 'border-red-500' : 'border-gray-300'}"
class="w-full p-2 border rounded {errors.email ? 'border-red-500' : 'border-gray-300'}"
bind="{email}"
placeholder="Enter email" />
{error && <p class="text-red-500 text-xs mt-1">{error.message}</p>}
{errors.email && <p class="text-red-500 text-xs mt-1">{errors.email.message}</p>}
<p class="text-sm text-gray-500 mt-1">
You can manage verified email addresses in your email settings.
</p>
@ -31,11 +31,11 @@
<controller name="bio">
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Bio</label>
<textarea class="w-full p-2 border rounded {error ? 'border-red-500' : 'border-gray-300'}"
<textarea class="w-full p-2 border rounded {errors.bio ? 'border-red-500' : 'border-gray-300'}"
bind="{bio}"
rows="4"
placeholder="Tell us a little bit about yourself"></textarea>
{error && <p class="text-red-500 text-xs mt-1">{error.message}</p>}
{errors.bio && <p class="text-red-500 text-xs mt-1">{errors.bio.message}</p>}
<p class="text-sm text-gray-500 mt-1">
You can @mention other users and organizations to link to them.
</p>
@ -58,10 +58,18 @@ import './style.css';
export default {
// Reactive state
username: '',
email: '',
bio: '',
error: null,
data() {
return {
username: '',
email: '',
bio: '',
errors: {
username: null,
email: null,
bio: null
}
}
},
// Validation schema
schema: z.object({
@ -77,10 +85,20 @@ export default {
email: this.email,
bio: this.bio
});
// Clear previous errors
this.errors = {
username: null,
email: null,
bio: null
};
if (result.success) {
callback(result.data);
} else {
this.error = result.error.errors[0];
result.error.errors.forEach(err => {
this.errors[err.path[0]] = err;
});
}
},

View file

@ -0,0 +1,18 @@
/* Settings page styles */
.settings-container {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.settings-section {
margin-bottom: 30px;
border-bottom: 1px solid #eee;
padding-bottom: 20px;
}
.settings-title {
font-size: 1.5rem;
margin-bottom: 15px;
color: #333;
}

View file

@ -71,3 +71,7 @@
.download-btn:hover {
background: #2563eb;
}
.dashboard-section {
padding: 1rem;
}

View file

@ -1,16 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<title>Dashboard</title>
<link rel="stylesheet" href="../css/global.css">
<link rel="stylesheet" href="dashboard.css">
</head>
<body>
<div id="main-content">
<div class="content-section active">
<main class="container p-4 space-y-4">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-foreground">Dashboard</h1>
<h1 class="text-2xl font-bold text-foreground">1Dashboard</h1>
<div class="date-range-picker"></div>
<button class="download-btn">Download</button>
</div>
@ -36,6 +28,4 @@
<script src="dashboard.js"></script>
<script src="components/date-range-picker.js"></script>
<script src="components/overview.js"></script>
<script src="components/recent-sales.js"></script>
</body>
</html>
<script src="components/recent-sales.js"></script>

View file

@ -21,10 +21,18 @@ document.addEventListener('DOMContentLoaded', () => {
]
};
// Initialize dashboard
// Initialize dashboard safely
function init() {
renderCards();
document.querySelector('.download-btn').addEventListener('click', handleDownload);
const ensure = setInterval(() => {
const main = document.querySelector('#main-content');
const section = main && main.querySelector('.cards-grid');
const btn = main && main.querySelector('.download-btn');
if (section && btn) {
clearInterval(ensure);
renderCards();
btn.addEventListener('click', handleDownload);
}
}, 100);
}
// Render dashboard cards
@ -54,5 +62,5 @@ document.addEventListener('DOMContentLoaded', () => {
}
// Initialize dashboard
init();
document.addEventListener('DOMContentLoaded',()=>{init();});
});

View file

@ -0,0 +1 @@
a {}

View file

@ -1,89 +1,73 @@
<!DOCTYPE html>
<html>
<head>
<title>Editor</title>
<link rel="stylesheet" href="../css/global.css">
<link rel="stylesheet" href="editor.css">
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<body x-data="editor" x-init="init()">
<div id="main-content">
<div class="content-section active">
<!-- Quick Access Toolbar -->
<div class="quick-access">
<button class="quick-access-btn" @click="document.execCommand('undo', false)">
<svg><!-- Undo icon --></svg>
<div id="main-content" x-data="editor">
<div class="content-section active">
<!-- Quick Access Toolbar -->
<div class="quick-access">
<button class="quick-access-btn" @click="document.execCommand('undo', false)">
<svg><!-- Undo icon --></svg>
</button>
<button class="quick-access-btn" @click="document.execCommand('redo', false)">
<svg><!-- Redo icon --></svg>
</button>
<div class="title-controls">
<input type="text" class="title-input" x-model="fileName" placeholder="Document name">
<button class="quick-access-btn" @click="saveDocument">
<svg><!-- Save icon --></svg>
</button>
<button class="quick-access-btn" @click="document.execCommand('redo', false)">
<svg><!-- Redo icon --></svg>
</button>
<div class="title-controls">
<input type="text" class="title-input" x-model="fileName" placeholder="Document name">
<button class="quick-access-btn" @click="saveDocument">
<svg><!-- Save icon --></svg>
</button>
</div>
</div>
</div>
<!-- Ribbon -->
<div class="ribbon">
<div class="ribbon-tabs">
<button class="ribbon-tab-button" :class="{ 'active': activeTab === 'home' }"
@click="setActiveTab('home')">Home</button>
<button class="ribbon-tab-button" :class="{ 'active': activeTab === 'insert' }"
@click="setActiveTab('insert')">Insert</button>
<button class="ribbon-tab-button" :class="{ 'active': activeTab === 'view' }"
@click="setActiveTab('view')">View</button>
</div>
<!-- Ribbon -->
<div class="ribbon">
<div class="ribbon-tabs">
<button class="ribbon-tab-button"
:class="{ 'active': activeTab === 'home' }"
@click="setActiveTab('home')">Home</button>
<button class="ribbon-tab-button"
:class="{ 'active': activeTab === 'insert' }"
@click="setActiveTab('insert')">Insert</button>
<button class="ribbon-tab-button"
:class="{ 'active': activeTab === 'view' }"
@click="setActiveTab('view')">View</button>
</div>
<div class="ribbon-content" x-show="activeTab === 'home'">
<div class="ribbon-group">
<div class="ribbon-group-title">Format</div>
<div class="ribbon-group-content">
<button class="ribbon-button" @click="formatBold">B</button>
<button class="ribbon-button" @click="formatItalic">I</button>
<button class="ribbon-button" @click="formatUnderline">U</button>
</div>
</div>
<div class="ribbon-group">
<div class="ribbon-group-title">Alignment</div>
<div class="ribbon-group-content">
<button class="ribbon-button" @click="alignLeft">Left</button>
<button class="ribbon-button" @click="alignCenter">Center</button>
<button class="ribbon-button" @click="alignRight">Right</button>
<button class="ribbon-button" @click="alignJustify">Justify</button>
</div>
<div class="ribbon-content" x-show="activeTab === 'home'">
<div class="ribbon-group">
<div class="ribbon-group-title">Format</div>
<div class="ribbon-group-content">
<button class="ribbon-button" @click="formatBold">B</button>
<button class="ribbon-button" @click="formatItalic">I</button>
<button class="ribbon-button" @click="formatUnderline">U</button>
</div>
</div>
</div>
<!-- Editor Area -->
<div class="editor-container">
<div class="editor-main">
<div class="pages-container">
<div class="page">
<div class="page-number">Page 1</div>
<div class="page-content" id="editor-content" contenteditable="true" x-html="content"></div>
</div>
<div class="ribbon-group">
<div class="ribbon-group-title">Alignment</div>
<div class="ribbon-group-content">
<button class="ribbon-button" @click="alignLeft">Left</button>
<button class="ribbon-button" @click="alignCenter">Center</button>
<button class="ribbon-button" @click="alignRight">Right</button>
<button class="ribbon-button" @click="alignJustify">Justify</button>
</div>
</div>
</div>
<!-- Status Bar -->
<div class="status-bar">
<div>Page <span x-text="pages.length"></span> of <span x-text="pages.length"></span></div>
<div class="zoom-controls">
<button @click="zoomOut">-</button>
<span x-text="zoom + '%'"></span>
<button @click="zoomIn">+</button>
</div>
</div>
</div>
</div>
<script src="editor.js"></script>
</body>
</html>
<!-- Editor Area -->
<div class="editor-container">
<div class="editor-main">
<div class="pages-container">
<div class="page">
<div class="page-number">Page 1</div>
<div class="page-content" id="editor-content" contenteditable="true" x-html="content"></div>
</div>
</div>
</div>
</div>
<!-- Status Bar -->
<div class="status-bar">
<div>Page <span x-text="pages.length"></span> of <span x-text="pages.length"></span></div>
<div class="zoom-controls">
<button @click="zoomOut">-</button>
<span x-text="zoom + '%'"></span>
<button @click="zoomIn">+</button>
</div>
</div>
</div>
</div>

View file

@ -41,5 +41,13 @@
<script src="drive/drive.js"></script>
<script src="tasks/tasks.js"></script>
<script src="mail/mail.js"></script>
<script src="dashboard/dashboard.js"></script>
<script src="editor/editor.js"></script>
<script src="player/player.js"></script>
<script src="paper/paper.js"></script>
<script src="settings/settings.js"></script>
<script src="tables/tables.js"></script>
<script src="news/news.js"></script>
</body>
</html>

View file

@ -7,7 +7,7 @@ const sections = {
player: 'player/player.html',
paper: 'paper/paper.html',
settings: 'settings/settings.html',
tables: 'tablesv2/tables.html',
tables: 'tables/tables.html',
news: 'news/news.html'
};
@ -21,8 +21,34 @@ async function switchSection(section) {
const mainContent = document.getElementById('main-content');
try {
const html = await loadSectionHTML(sections[section]);
const htmlPath = sections[section];
console.log('Loading section:', section, 'from', htmlPath);
const cssPath =
htmlPath.replace('.html', '.css');
// Remove any existing section CSS
document.querySelectorAll('link[data-section-css]').forEach(link => link.remove());
// Load CSS first
const cssLink = document.createElement('link');
cssLink.rel = 'stylesheet';
cssLink.href = cssPath;
cssLink.setAttribute('data-section-css', 'true');
document.head.appendChild(cssLink);
// First load HTML
const html = await loadSectionHTML(htmlPath);
mainContent.innerHTML = html;
// Then load JS after HTML is inserted
const jsPath = htmlPath.replace('.html', '.js');
const existingScript = document.querySelector(`script[src="${jsPath}"]`);
if (!existingScript) {
const script = document.createElement('script');
script.src = jsPath;
script.defer = true;
document.body.appendChild(script);
}
window.history.pushState({}, '', `#${section}`);
Alpine.initTree(mainContent);
} catch (err) {

View file

@ -0,0 +1 @@
a {}

View file

@ -1,11 +1,3 @@
<!DOCTYPE html>
<html>
<head>
<title>News</title>
<link rel="stylesheet" href="../css/global.css">
<link rel="stylesheet" href="news.css">
</head>
<body>
<div id="main-content">
<div class="content-section active">
<div class="panel">
@ -14,6 +6,3 @@
</div>
</div>
</div>
<script src="news.js"></script>
</body>
</html>

View file

@ -1,77 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<title>Paper</title>
<link rel="stylesheet" href="../css/global.css">
<link rel="stylesheet" href="paper.css">
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<body x-data="paper">
<div id="main-content">
<div class="content-section active">
<div class="max-w-4xl mx-auto">
<!-- Paper Shadow Effect -->
<div class="mx-4 my-8 bg-card rounded-lg shadow-2xl shadow-black/20 border border-border">
<div class="editor-content"
x-ref="editor"
contenteditable="true"
x-init="initEditor()"
class="min-h-[calc(100vh-12rem)] p-8 prose max-w-none focus:outline-none">
Start writing your thoughts here...
</div>
</div>
</div>
<!-- Floating Toolbar -->
<div class="floating-toolbar" x-show="showToolbar" x-transition>
<div class="flex items-center bg-card border border-border rounded-lg shadow-lg p-1">
<!-- Text Formatting -->
<button @click="formatText('bold')"
class="p-2 rounded hover:bg-accent transition-colors"
:class="{'bg-primary text-primary-foreground': isActive('bold')}">
B
</button>
<button @click="formatText('italic')"
class="p-2 rounded hover:bg-accent transition-colors"
:class="{'bg-primary text-primary-foreground': isActive('italic')}">
I
</button>
<button @click="formatText('underline')"
class="p-2 rounded hover:bg-accent transition-colors"
:class="{'bg-primary text-primary-foreground': isActive('underline')}">
U
</button>
<div class="w-px h-6 bg-border mx-1"></div>
<!-- Text Alignment -->
<button @click="alignText('left')"
class="p-2 rounded hover:bg-accent transition-colors"
:class="{'bg-primary text-primary-foreground': isAligned('left')}">
Left
</button>
<button @click="alignText('center')"
class="p-2 rounded hover:bg-accent transition-colors"
:class="{'bg-primary text-primary-foreground': isAligned('center')}">
Center
</button>
<button @click="alignText('right')"
class="p-2 rounded hover:bg-accent transition-colors"
:class="{'bg-primary text-primary-foreground': isAligned('right')}">
Right
</button>
<div class="w-px h-6 bg-border mx-1"></div>
<!-- Link -->
<button @click="addLink"
class="p-2 rounded hover:bg-accent transition-colors">
Link
</button>
<div id="main-content">
<div class="content-section active">
<div class="max-w-4xl mx-auto">
<!-- Paper Shadow Effect -->
<div class="mx-4 my-8 bg-card rounded-lg shadow-2xl shadow-black/20 border border-border">
<div class="editor-content" x-ref="editor" contenteditable="true" x-init="initEditor()"
class="min-h-[calc(100vh-12rem)] p-8 prose max-w-none focus:outline-none">
Start writing your thoughts here...
</div>
</div>
</div>
<!-- Floating Toolbar -->
<div class="floating-toolbar" x-show="showToolbar" x-transition>
<div class="flex items-center bg-card border border-border rounded-lg shadow-lg p-1">
<!-- Text Formatting -->
<button @click="formatText('bold')" class="p-2 rounded hover:bg-accent transition-colors"
:class="{'bg-primary text-primary-foreground': isActive('bold')}">
B
</button>
<button @click="formatText('italic')" class="p-2 rounded hover:bg-accent transition-colors"
:class="{'bg-primary text-primary-foreground': isActive('italic')}">
I
</button>
<button @click="formatText('underline')" class="p-2 rounded hover:bg-accent transition-colors"
:class="{'bg-primary text-primary-foreground': isActive('underline')}">
U
</button>
<div class="w-px h-6 bg-border mx-1"></div>
<!-- Text Alignment -->
<button @click="alignText('left')" class="p-2 rounded hover:bg-accent transition-colors"
:class="{'bg-primary text-primary-foreground': isAligned('left')}">
Left
</button>
<button @click="alignText('center')" class="p-2 rounded hover:bg-accent transition-colors"
:class="{'bg-primary text-primary-foreground': isAligned('center')}">
Center
</button>
<button @click="alignText('right')" class="p-2 rounded hover:bg-accent transition-colors"
:class="{'bg-primary text-primary-foreground': isAligned('right')}">
Right
</button>
<div class="w-px h-6 bg-border mx-1"></div>
<!-- Link -->
<button @click="addLink" class="p-2 rounded hover:bg-accent transition-colors">
Link
</button>
</div>
</div>
</div>
<script src="paper.js"></script>
</body>
</html>
</div>

View file

@ -1,26 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>Player</title>
<link rel="stylesheet" href="../css/global.css">
<link rel="stylesheet" href="player.css">
</head>
<body>
<div id="main-content">
<div class="content-section active">
<div class="player-container">
<div class="video-container">
<!-- Video element will be inserted here -->
</div>
<div class="player-controls">
<button class="play-btn">Play</button>
<input type="range" class="slider progress-slider" min="0" max="100" value="0">
<span class="time-display">0:00 / 0:00</span>
<input type="range" class="slider volume-slider" min="0" max="100" value="80">
</div>
<div id="main-content">
<div class="content-section active">
<div class="player-container">
<div class="video-container">
<!-- Video element will be inserted here -->
</div>
<div class="player-controls">
<button class="play-btn">Play</button>
<input type="range" class="slider progress-slider" min="0" max="100" value="0">
<span class="time-display">0:00 / 0:00</span>
<input type="range" class="slider volume-slider" min="0" max="100" value="80">
</div>
</div>
</div>
<script src="player.js"></script>
</body>
</html>
</div>

View file

@ -1,61 +1,63 @@
// Player module JavaScript
document.addEventListener('DOMContentLoaded', () => {
const playerContainer = document.querySelector('.player-container');
const videoContainer = document.querySelector('.video-container');
const playBtn = document.querySelector('.play-btn');
const progressSlider = document.querySelector('.progress-slider');
const volumeSlider = document.querySelector('.volume-slider');
const timeDisplay = document.querySelector('.time-display');
// Create video element
const video = document.createElement('video');
video.src = ''; // Will be set when loading media
video.controls = false;
videoContainer.appendChild(video);
// Play/Pause toggle
playBtn.addEventListener('click', () => {
if (video.paused) {
video.play();
playBtn.textContent = 'Pause';
} else {
video.pause();
playBtn.textContent = 'Play';
const ready = setInterval(() => {
const container = document.querySelector('.player-container');
if (!container) return;
clearInterval(ready);
const videoContainer = document.querySelector('.video-container');
const playBtn = document.querySelector('.play-btn');
const progressSlider = document.querySelector('.progress-slider');
const volumeSlider = document.querySelector('.volume-slider');
const timeDisplay = document.querySelector('.time-display');
const ensure = setInterval(() => {
const container = document.querySelector('.video-container');
if (container) {
clearInterval(ensure);
const video = document.createElement('video');
video.src = '';
video.controls = false;
container.appendChild(video);
setupControls(video);
}
}, 50);
function setupControls(video) {
const playBtn = document.querySelector('.play-btn');
const progressSlider = document.querySelector('.progress-slider');
const volumeSlider = document.querySelector('.volume-slider');
const timeDisplay = document.querySelector('.time-display');
playBtn.addEventListener('click', () => {
if (video.paused) {
video.play();
playBtn.textContent = 'Pause';
} else {
video.pause();
playBtn.textContent = 'Play';
}
});
video.addEventListener('timeupdate', () => {
const progress = (video.currentTime / video.duration) * 100;
progressSlider.value = progress || 0;
updateTimeDisplay(video, timeDisplay);
});
progressSlider.addEventListener('input', () => {
const seekTime = (progressSlider.value / 100) * video.duration;
video.currentTime = seekTime;
});
volumeSlider.addEventListener('input', () => {
video.volume = volumeSlider.value / 100;
});
window.loadMedia = (src) => {
video.src = src;
video.load();
};
}
function updateTimeDisplay(video, display) {
const formatTime = (seconds) => {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
};
display.textContent = `${formatTime(video.currentTime)} / ${formatTime(video.duration)}`;
}
});
// Update progress slider
video.addEventListener('timeupdate', () => {
const progress = (video.currentTime / video.duration) * 100;
progressSlider.value = progress || 0;
updateTimeDisplay();
});
// Seek video
progressSlider.addEventListener('input', () => {
const seekTime = (progressSlider.value / 100) * video.duration;
video.currentTime = seekTime;
});
// Volume control
volumeSlider.addEventListener('input', () => {
video.volume = volumeSlider.value / 100;
});
// Format time display
function updateTimeDisplay() {
const formatTime = (seconds) => {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
};
timeDisplay.textContent = `${formatTime(video.currentTime)} / ${formatTime(video.duration)}`;
}
// Load media (to be called externally)
window.loadMedia = (src) => {
video.src = src;
video.load();
};
});

View file

@ -1,74 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<title>Settings</title>
<link rel="stylesheet" href="../css/global.css">
<link rel="stylesheet" href="settings.css">
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<body x-data="settings">
<div id="main-content">
<div class="content-section active">
<div class="space-y-6 p-4">
<div>
<h2 class="text-lg font-medium">Profile</h2>
<p class="text-sm text-gray-500"></p>
<div id="main-content">
<div class="content-section active">
<div class="space-y-6 p-4">
<div>
<h2 class="text-lg font-medium">Profile</h2>
<p class="text-sm text-gray-500"></p>
</div>
<div class="border-t border-gray-200 my-4"></div>
<div class="space-y-4">
<!-- Username Field -->
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Username</label>
<input class="w-full p-2 border rounded" x-model="username" placeholder="Enter username" />
<template x-if="errors.username">
<p class="text-red-500 text-xs mt-1" x-text="errors.username"></p>
</template>
<p class="text-sm text-gray-500 mt-1">
This is your public display name. It can be your real name or a pseudonym.
</p>
</div>
<div class="border-t border-gray-200 my-4"></div>
<div class="space-y-4">
<!-- Username Field -->
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Username</label>
<input class="w-full p-2 border rounded"
x-model="username"
placeholder="Enter username" />
<template x-if="errors.username">
<p class="text-red-500 text-xs mt-1" x-text="errors.username"></p>
</template>
<p class="text-sm text-gray-500 mt-1">
This is your public display name. It can be your real name or a pseudonym.
</p>
</div>
<!-- Email Field -->
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Email</label>
<input type="email"
class="w-full p-2 border rounded"
x-model="email"
placeholder="Enter email" />
<template x-if="errors.email">
<p class="text-red-500 text-xs mt-1" x-text="errors.email"></p>
</template>
<p class="text-sm text-gray-500 mt-1">
You can manage verified email addresses in your email settings.
</p>
</div>
<!-- Bio Field -->
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Bio</label>
<textarea class="w-full p-2 border rounded"
x-model="bio"
rows="4"
placeholder="Tell us a little bit about yourself"></textarea>
<template x-if="errors.bio">
<p class="text-red-500 text-xs mt-1" x-text="errors.bio"></p>
</template>
<p class="text-sm text-gray-500 mt-1">
You can @mention other users and organizations to link to them.
</p>
</div>
<button class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
@click="submitForm">
Update profile
</button>
<!-- Email Field -->
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Email</label>
<input type="email" class="w-full p-2 border rounded" x-model="email" placeholder="Enter email" />
<template x-if="errors.email">
<p class="text-red-500 text-xs mt-1" x-text="errors.email"></p>
</template>
<p class="text-sm text-gray-500 mt-1">
You can manage verified email addresses in your email settings.
</p>
</div>
<!-- Bio Field -->
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Bio</label>
<textarea class="w-full p-2 border rounded" x-model="bio" rows="4"
placeholder="Tell us a little bit about yourself"></textarea>
<template x-if="errors.bio">
<p class="text-red-500 text-xs mt-1" x-text="errors.bio"></p>
</template>
<p class="text-sm text-gray-500 mt-1">
You can @mention other users and organizations to link to them.
</p>
</div>
<button class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600" @click="submitForm">
Update profile
</button>
</div>
</div>
</div>
<script src="settings.js"></script>
</body>
</html>
</div>

View file

@ -1,12 +1,3 @@
<!DOCTYPE html>
<html>
<head>
<title>Tables</title>
<link rel="stylesheet" href="../css/global.css">
<link rel="stylesheet" href="tables.css">
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<body x-data="tablesApp()" x-init="init()">
<div id="main-content">
<div class="content-section active">
<div class="app-container">
@ -62,6 +53,3 @@
</div>
</div>
</div>
<script src="tables.js"></script>
</body>
</html>

View file

@ -0,0 +1 @@
a {}