fix: modification visuel du frontend au niveau du formulaire de contact pour qu'il s'armonise avec le css global.

fix : update du backend avec ajout de la suppression d'un projet.
This commit is contained in:
toine 2025-10-04 11:03:08 +02:00
parent ef1ba911d9
commit 542e00482b
9 changed files with 68 additions and 15 deletions

View file

@ -20,7 +20,7 @@ app.config["API_KEY"] = os.getenv("API_KEY")
@app.route('/') @app.route('/')
def get_home(): def get_home():
return jsonify({ return jsonify({
"return": "Welcome to API" "return": f"Welcome to API."
}) })
app.register_blueprint(projects_bp) app.register_blueprint(projects_bp)

View file

@ -46,3 +46,13 @@ def create_project():
added = add_entry(PROJECTS_FILE, data) added = add_entry(PROJECTS_FILE, data)
return jsonify(added), 201 return jsonify(added), 201
@projects_bp.route('/<project_id>', methods=['DELETE'])
def delete_project(project_id):
projects = load_data(PROJECTS_FILE)
project = next((p for p in projects if str(p.get('id')) == project_id), None)
if project:
deleted = delete_entry(PROJECTS_FILE, project_id)
return jsonify(deleted), 200
return jsonify({"error": "Project not found"}), 404

View file

@ -26,8 +26,16 @@ def update_entry(filename, entry_id, new_entry):
return new_entry return new_entry
return None return None
def delete_entry(filename, entry_id): def delete_entry(filename, project_id):
data = load_json(filename) # 1. Charger les données
new_data = [item for item in data if item.get('id') != entry_id] with open(filename, "r", encoding="utf-8") as f:
save_json(filename, new_data) projects = json.load(f)
return len(data) != len(new_data)
# 2. Filtrer pour supprimer le projet avec l'ID donné
updated_projects = [p for p in projects if p.get('id') != int(project_id)]
# 3. Sauvegarder les données mises à jour
with open(filename, "w", encoding="utf-8") as f:
json.dump(updated_projects, f, indent=2, ensure_ascii=False)
return {"status": "success", "deleted_id": project_id}

View file

@ -45,7 +45,7 @@ export default function Header() {
<ul> <ul>
<li><Link href="/projects" >Projets</Link></li> <li><Link href="/projects" >Projets</Link></li>
<li><Link href="/competences">Compétences</Link></li> <li><Link href="/competences">Compétences</Link></li>
<li><Link href="#blog">Blog Voyage</Link></li> <li><Link href="https://toinesensei.itch.io/" target="_blank">Itch.io</Link></li>
<li><Link href="/contact">Contact</Link></li> <li><Link href="/contact">Contact</Link></li>
<li><Link href="https://fr.malt.be/profile/anthonyviolet1" target="_blank">Malt</Link></li> <li><Link href="https://fr.malt.be/profile/anthonyviolet1" target="_blank">Malt</Link></li>
<li><Link href="https://www.linkedin.com/in/anthony-violet/" target="_blank">LinkedIn</Link></li> <li><Link href="https://www.linkedin.com/in/anthony-violet/" target="_blank">LinkedIn</Link></li>

View file

@ -3,9 +3,12 @@
/* Couleurs de base (clair) */ /* Couleurs de base (clair) */
--color-bg: #ffffff; --color-bg: #ffffff;
--color-surface: #ffffff; --color-surface: #ffffff;
/* Légèrement plus foncé pour champs en clair */
--color-form-surface: color-mix(in oklab, var(--color-surface) 94%, black);
--color-text: #1f2937; --color-text: #1f2937;
--color-muted: #6b7280; --color-muted: #6b7280;
--color-border: #efefef; --color-border: #efefef;
--color-border-strong: #d1d5db;
--color-accent: #ff6b35; --color-accent: #ff6b35;
/* Variantes daccent */ /* Variantes daccent */
@ -43,9 +46,12 @@
:root { :root {
--color-bg: #1f2937; --color-bg: #1f2937;
--color-surface: #0e141b; --color-surface: #0e141b;
/* Légèrement plus clair pour champs en sombre */
--color-form-surface: color-mix(in oklab, var(--color-surface) 92%, white);
--color-text: #e5e7eb; --color-text: #e5e7eb;
--color-muted: #9ca3af; --color-muted: #9ca3af;
--color-border: #0b0f14; --color-border: #0b0f14;
--color-border-strong: #263241;
/* accent inchangé pour la cohérence de marque */ /* accent inchangé pour la cohérence de marque */
} }
} }
@ -54,6 +60,8 @@
html[data-theme="light"] { html[data-theme="light"] {
--color-bg: #ffffff; --color-bg: #ffffff;
--color-surface: #FFF7F7; --color-surface: #FFF7F7;
/* Légèrement plus foncé pour champs en clair */
--color-form-surface: color-mix(in oklab, var(--color-surface) 94%, black);
--color-text: #1f2937; --color-text: #1f2937;
--color-muted: #6b7280; --color-muted: #6b7280;
--color-border: #efefef; --color-border: #efefef;
@ -62,6 +70,8 @@ html[data-theme="light"] {
html[data-theme="dark"] { html[data-theme="dark"] {
--color-bg: #1f2937; --color-bg: #1f2937;
--color-surface: #0e141b; --color-surface: #0e141b;
/* Légèrement plus clair pour champs en sombre */
--color-form-surface: color-mix(in oklab, var(--color-surface) 92%, white);
--color-text: #e5e7eb; --color-text: #e5e7eb;
--color-muted: #9ca3af; --color-muted: #9ca3af;
--color-border: #0b0f14; --color-border: #0b0f14;

View file

@ -15,12 +15,13 @@ select,
textarea { textarea {
width: 100%; width: 100%;
max-width: 640px; max-width: 640px;
background: #ffffff; background: var(--color-form-surface, var(--color-surface, #ffffff));
color: var(--color-text, #1f2937); color: var(--color-text, #1f2937);
border: 1px solid var(--color-border, #efefef); border: 1px solid var(--color-border-strong, #d1d5db);
border-radius: var(--radius-sm, 6px); border-radius: var(--radius-sm, 6px);
padding: 0.625rem 0.75rem; padding: 0.625rem 0.75rem;
outline: none; outline: none;
box-shadow: var(--shadow-sm, 0 1px 2px rgba(0, 0, 0, 0.06));
transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease; transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
} }
@ -28,6 +29,8 @@ input:hover,
select:hover, select:hover,
textarea:hover { textarea:hover {
border-color: var(--color-accent-100, #ffe4d6); border-color: var(--color-accent-100, #ffe4d6);
background: color-mix(in oklab, var(--color-surface, #ffffff) 94%, var(--color-accent-50, #fff4ec));
box-shadow: var(--shadow-md, 0 2px 10px rgba(0, 0, 0, 0.08));
} }
textarea { textarea {
@ -45,7 +48,7 @@ select:focus,
textarea:focus { textarea:focus {
border-color: var(--color-accent, #ff6b35); border-color: var(--color-accent, #ff6b35);
box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-accent, #ff6b35) 22%, transparent); box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-accent, #ff6b35) 22%, transparent);
background: #fff; background: var(--color-form-surface, var(--color-surface, #ffffff));
} }
/* Boutons */ /* Boutons */

View file

@ -121,6 +121,10 @@ hr {
color: var(--color-muted, #6b7280); color: var(--color-muted, #6b7280);
} }
footer {
background: var(--color-surface);
}
/* Fin: préférences de mouvement */ /* Fin: préférences de mouvement */
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
* { * {
@ -129,3 +133,21 @@ hr {
scroll-behavior: auto !important; scroll-behavior: auto !important;
} }
} }
/* Sticky footer (hidden at top, visible on scroll) */
.sticky-footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
transform: translateY(100%);
transition: transform 0.3s ease;
z-index: 40;
pointer-events: none; /* prevent blocking clicks when hidden */
}
.sticky-footer.is-visible {
transform: translateY(0);
box-shadow: 0 -6px 20px rgba(0, 0, 0, 0.08);
pointer-events: auto;
}

View file

@ -9,6 +9,7 @@ import "./css/links.css";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import Header from "./components/Header"; import Header from "./components/Header";
import Footer from "./components/Footer";
export default function RootLayout({ children }: { children: ReactNode }) { export default function RootLayout({ children }: { children: ReactNode }) {
return ( return (
@ -17,9 +18,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
<Header /> <Header />
<main>{children}</main> <main>{children}</main>
<footer> <Footer />
<p>© 2025 - Toine</p>
</footer>
</body> </body>
</html> </html>
); );

View file

@ -67,6 +67,7 @@ export default function NewProjectPage() {
</select> </select>
) : ( ) : (
<input <input
type={key === "url" || key === "image" || key === "source" ? "url" : "text"}
name={key} name={key}
value={formData[key as keyof typeof formData]} value={formData[key as keyof typeof formData]}
onChange={handleChange} onChange={handleChange}