Skip to content

Commit 295d1c3

Browse files
author
mcarbonell
committed
feat: Day 6 - Modern tool pages with toast notifications and progress indicators
1 parent 9fdbedb commit 295d1c3

File tree

5 files changed

+380
-18
lines changed

5 files changed

+380
-18
lines changed

NEXT_STEPS.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@
3434
- ✅ Section headers con barra lateral
3535
- ✅ Responsive mobile optimizado
3636

37+
### Tool Pages (Día 6)
38+
- ✅ Layout moderno con tool-header
39+
- ✅ Botón back con icono Material
40+
- ✅ Drop zones mejoradas con hover
41+
- ✅ Form controls modernos con focus states
42+
- ✅ Toast notification system (toast.js)
43+
- ✅ Progress indicators con shimmer
44+
- ✅ Spinner animations
45+
- ✅ Template base.html actualizado
46+
3747
---
3848

3949
## 🔜 Ahora Mismo (Desplegando)
@@ -50,15 +60,7 @@
5060

5161
### 1. Continuar Diseño (Día 4-7)
5262

53-
**Día 6: Tool Pages** ⭅️ SIGUIENTE
54-
```
55-
□ Layout herramientas individual
56-
□ Botones y controles modernos
57-
□ Toast notifications
58-
□ Progress indicators
59-
```
60-
61-
**Día 7: Refinamiento**
63+
**Día 7: Refinamiento** ⬅️ SIGUIENTE
6264
```
6365
□ Animaciones finales
6466
□ Dark mode toggle (opcional)

web/css/style-v2.css

Lines changed: 270 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,276 @@ section h3::before {
681681
}
682682
}
683683

684+
/* ====================
685+
TOOL PAGES
686+
==================== */
687+
688+
.tool-header {
689+
margin-bottom: var(--spacing-xl);
690+
padding-bottom: var(--spacing-lg);
691+
border-bottom: 2px solid var(--border-light);
692+
}
693+
694+
.tool-header h1 {
695+
font-size: var(--text-3xl);
696+
font-weight: var(--font-bold);
697+
color: var(--text-primary);
698+
margin-bottom: var(--spacing-sm);
699+
}
700+
701+
.tool-header .text-muted {
702+
font-size: var(--text-base);
703+
color: var(--text-secondary);
704+
}
705+
706+
/* Drop Zone */
707+
.drop-zone {
708+
border: 2px dashed var(--border-medium);
709+
border-radius: var(--radius-lg);
710+
padding: var(--spacing-2xl);
711+
text-align: center;
712+
background: var(--bg-light);
713+
transition: all var(--transition-base);
714+
cursor: pointer;
715+
}
716+
717+
.drop-zone:hover {
718+
border-color: var(--primary);
719+
background: rgba(19, 164, 236, 0.05);
720+
}
721+
722+
.drop-zone.drag-over {
723+
border-color: var(--primary);
724+
background: rgba(19, 164, 236, 0.1);
725+
transform: scale(1.02);
726+
}
727+
728+
.drop-message {
729+
color: var(--text-secondary);
730+
font-size: var(--text-base);
731+
display: flex;
732+
flex-direction: column;
733+
align-items: center;
734+
gap: var(--spacing-sm);
735+
}
736+
737+
/* Form Controls */
738+
.form-control,
739+
.form-select {
740+
border: 1px solid var(--border-medium);
741+
border-radius: var(--radius-md);
742+
padding: var(--spacing-sm) var(--spacing-md);
743+
font-size: var(--text-base);
744+
transition: all var(--transition-fast);
745+
}
746+
747+
.form-control:focus,
748+
.form-select:focus {
749+
border-color: var(--primary);
750+
box-shadow: 0 0 0 3px rgba(19, 164, 236, 0.1);
751+
outline: none;
752+
}
753+
754+
.input-group-text {
755+
background: var(--bg-light);
756+
border: 1px solid var(--border-medium);
757+
color: var(--text-secondary);
758+
font-weight: var(--font-medium);
759+
}
760+
761+
/* Buttons in tool pages */
762+
.btn {
763+
font-weight: var(--font-medium);
764+
padding: var(--spacing-sm) var(--spacing-lg);
765+
border-radius: var(--radius-md);
766+
transition: all var(--transition-base);
767+
display: inline-flex;
768+
align-items: center;
769+
gap: var(--spacing-sm);
770+
}
771+
772+
.btn-primary {
773+
background: var(--primary);
774+
border: none;
775+
color: white;
776+
}
777+
778+
.btn-primary:hover {
779+
background: var(--primary-hover);
780+
transform: translateY(-2px);
781+
box-shadow: var(--shadow-lg);
782+
}
783+
784+
.btn-secondary {
785+
background: var(--bg-light);
786+
border: 1px solid var(--border-medium);
787+
color: var(--text-primary);
788+
}
789+
790+
.btn-secondary:hover {
791+
background: var(--bg-white);
792+
border-color: var(--primary);
793+
color: var(--primary);
794+
}
795+
796+
/* ====================
797+
TOAST NOTIFICATIONS
798+
==================== */
799+
800+
.toast-container {
801+
position: fixed;
802+
top: 20px;
803+
right: 20px;
804+
z-index: var(--z-tooltip);
805+
display: flex;
806+
flex-direction: column;
807+
gap: var(--spacing-sm);
808+
pointer-events: none;
809+
}
810+
811+
.toast {
812+
background: white;
813+
border-radius: var(--radius-lg);
814+
padding: var(--spacing-md) var(--spacing-lg);
815+
box-shadow: var(--shadow-xl);
816+
display: flex;
817+
align-items: center;
818+
gap: var(--spacing-md);
819+
min-width: 300px;
820+
max-width: 400px;
821+
pointer-events: all;
822+
animation: slideInRight 0.3s ease-out;
823+
border-left: 4px solid var(--primary);
824+
}
825+
826+
.toast.success {
827+
border-left-color: var(--success);
828+
}
829+
830+
.toast.error {
831+
border-left-color: var(--error);
832+
}
833+
834+
.toast.warning {
835+
border-left-color: var(--warning);
836+
}
837+
838+
.toast.info {
839+
border-left-color: var(--info);
840+
}
841+
842+
.toast-icon {
843+
font-size: var(--text-2xl);
844+
flex-shrink: 0;
845+
}
846+
847+
.toast.success .toast-icon { color: var(--success); }
848+
.toast.error .toast-icon { color: var(--error); }
849+
.toast.warning .toast-icon { color: var(--warning); }
850+
.toast.info .toast-icon { color: var(--info); }
851+
852+
.toast-content {
853+
flex: 1;
854+
}
855+
856+
.toast-title {
857+
font-weight: var(--font-semibold);
858+
color: var(--text-primary);
859+
margin-bottom: 2px;
860+
}
861+
862+
.toast-message {
863+
font-size: var(--text-sm);
864+
color: var(--text-secondary);
865+
}
866+
867+
.toast-close {
868+
background: none;
869+
border: none;
870+
color: var(--text-muted);
871+
cursor: pointer;
872+
padding: 0;
873+
font-size: var(--text-xl);
874+
line-height: 1;
875+
transition: color var(--transition-fast);
876+
}
877+
878+
.toast-close:hover {
879+
color: var(--text-primary);
880+
}
881+
882+
@keyframes slideInRight {
883+
from {
884+
transform: translateX(400px);
885+
opacity: 0;
886+
}
887+
to {
888+
transform: translateX(0);
889+
opacity: 1;
890+
}
891+
}
892+
893+
/* ====================
894+
PROGRESS INDICATORS
895+
==================== */
896+
897+
.progress {
898+
height: 8px;
899+
background: var(--bg-light);
900+
border-radius: var(--radius-full);
901+
overflow: hidden;
902+
position: relative;
903+
}
904+
905+
.progress-bar {
906+
height: 100%;
907+
background: linear-gradient(90deg, var(--primary), var(--primary-light));
908+
border-radius: var(--radius-full);
909+
transition: width var(--transition-base);
910+
position: relative;
911+
overflow: hidden;
912+
}
913+
914+
.progress-bar::after {
915+
content: '';
916+
position: absolute;
917+
top: 0;
918+
left: 0;
919+
right: 0;
920+
bottom: 0;
921+
background: linear-gradient(
922+
90deg,
923+
transparent,
924+
rgba(255, 255, 255, 0.3),
925+
transparent
926+
);
927+
animation: shimmer 2s infinite;
928+
}
929+
930+
@keyframes shimmer {
931+
0% { transform: translateX(-100%); }
932+
100% { transform: translateX(100%); }
933+
}
934+
935+
.spinner {
936+
width: 40px;
937+
height: 40px;
938+
border: 4px solid var(--bg-light);
939+
border-top-color: var(--primary);
940+
border-radius: 50%;
941+
animation: spin 0.8s linear infinite;
942+
}
943+
944+
@keyframes spin {
945+
to { transform: rotate(360deg); }
946+
}
947+
948+
.spinner-sm {
949+
width: 20px;
950+
height: 20px;
951+
border-width: 2px;
952+
}
953+
684954
/* ====================
685955
ACCESSIBILITY
686956
==================== */
@@ -695,7 +965,6 @@ section h3::before {
695965
}
696966
}
697967

698-
/* Focus visible for keyboard navigation */
699968
*:focus-visible {
700969
outline: 2px solid var(--primary);
701970
outline-offset: 2px;

web/js/toast.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* Toast Notification System
3+
* Simple, modern toast notifications for FastTools
4+
*/
5+
6+
class Toast {
7+
constructor() {
8+
this.container = this.createContainer();
9+
}
10+
11+
createContainer() {
12+
let container = document.querySelector('.toast-container');
13+
if (!container) {
14+
container = document.createElement('div');
15+
container.className = 'toast-container';
16+
document.body.appendChild(container);
17+
}
18+
return container;
19+
}
20+
21+
show(message, type = 'info', duration = 3000) {
22+
const toast = document.createElement('div');
23+
toast.className = `toast ${type}`;
24+
25+
const icons = {
26+
success: 'check_circle',
27+
error: 'error',
28+
warning: 'warning',
29+
info: 'info'
30+
};
31+
32+
toast.innerHTML = `
33+
<span class="material-symbols-outlined toast-icon">${icons[type]}</span>
34+
<div class="toast-content">
35+
<div class="toast-message">${message}</div>
36+
</div>
37+
<button class="toast-close" aria-label="Close">×</button>
38+
`;
39+
40+
const closeBtn = toast.querySelector('.toast-close');
41+
closeBtn.addEventListener('click', () => this.remove(toast));
42+
43+
this.container.appendChild(toast);
44+
45+
if (duration > 0) {
46+
setTimeout(() => this.remove(toast), duration);
47+
}
48+
49+
return toast;
50+
}
51+
52+
remove(toast) {
53+
toast.style.animation = 'slideInRight 0.3s ease-out reverse';
54+
setTimeout(() => {
55+
if (toast.parentNode) {
56+
toast.parentNode.removeChild(toast);
57+
}
58+
}, 300);
59+
}
60+
61+
success(message, duration) {
62+
return this.show(message, 'success', duration);
63+
}
64+
65+
error(message, duration) {
66+
return this.show(message, 'error', duration);
67+
}
68+
69+
warning(message, duration) {
70+
return this.show(message, 'warning', duration);
71+
}
72+
73+
info(message, duration) {
74+
return this.show(message, 'info', duration);
75+
}
76+
}
77+
78+
// Global instance
79+
window.toast = new Toast();

0 commit comments

Comments
 (0)