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:
parent
ef1ba911d9
commit
542e00482b
9 changed files with 68 additions and 15 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -45,4 +45,14 @@ def create_project():
|
||||||
data['id'] = new_id
|
data['id'] = new_id
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -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}
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 d’accent */
|
/* Variantes d’accent */
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
* {
|
* {
|
||||||
|
|
@ -128,4 +132,22 @@ hr {
|
||||||
transition: none !important;
|
transition: none !important;
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue