Files
2026-05-04 19:15:00 +02:00

423 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>{% if modo == 'ciudad' %}madcraig — {{ city }}{% else %}madcraig{% endif %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-body: #f5f7fa;
--bg-card: #ffffff;
--text-main: #1f2937;
--text-muted: #6b7280;
--border: #e5e7eb;
--radius: 12px;
--radius-sm: 8px;
--shadow: 0 1px 3px rgba(0,0,0,0.05), 0 1px 2px rgba(0,0,0,0.03);
--shadow-hover: 0 4px 6px rgba(0,0,0,0.05), 0 2px 4px rgba(0,0,0,0.03);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: "JetBrains Mono", "JetBrainsMono Nerd Font", "Fira Code", monospace;
background: var(--bg-body);
color: var(--text-main);
margin: 0;
padding: 40px 24px;
line-height: 1.55;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-y: scroll;
}
.container {
max-width: 860px;
margin: 0 auto;
}
h1 {
font-weight: 700;
font-size: 1.75rem;
margin: 0 0 6px 0;
letter-spacing: -0.5px;
color: #111827;
}
h1 a {
font-size: 1em;
}
.subtitle {
color: var(--text-muted);
font-size: 0.95rem;
margin: 0 0 20px 0;
}
h2 {
font-weight: 600;
font-size: 1.15rem;
margin: 32px 0 12px 0;
color: #111827;
}
/* City list */
.city-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 12px;
margin-top: 24px;
}
.city-card {
display: block;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 16px 20px;
text-decoration: none;
color: var(--text-main);
font-weight: 600;
font-size: 0.95em;
box-shadow: var(--shadow);
transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
}
.city-card:hover {
box-shadow: var(--shadow-hover);
transform: translateY(-1px);
border-color: #3b82f6;
text-decoration: none;
}
.city-card span {
color: var(--text-muted);
font-weight: 400;
font-size: 0.85em;
display: block;
margin-top: 4px;
}
/* Cards */
.anuncio {
background: var(--bg-card);
border: 1px solid var(--border);
border-left: 4px solid #9ca3af;
border-radius: var(--radius);
padding: 16px 18px;
margin: 14px 0;
box-shadow: var(--shadow);
transition: box-shadow 0.2s ease, transform 0.2s ease;
font-size: 0.9em;
}
.anuncio:hover {
box-shadow: var(--shadow-hover);
transform: translateY(-1px);
}
.meta {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 8px;
font-size: 0.82em;
color: var(--text-muted);
}
.meta form {
display: inline;
margin: 0;
padding: 0;
border: none;
background: transparent;
}
.tag {
display: inline-block;
padding: 3px 8px;
border-radius: 6px;
font-size: 0.78em;
font-weight: 600;
background: #f3f4f6;
color: #4b5563;
border: none;
margin-right: 6px;
letter-spacing: 0.2px;
}
/* Colores por categoría */
.tag[data-categoria="general"] { background: #dbeafe; color: #1e40af; }
.tag[data-categoria="trabajo"] { background: #dcfce7; color: #166534; }
.tag[data-categoria="venta"] { background: #ffedd5; color: #9a3412; }
.tag[data-categoria="servicios"] { background: #f3e8ff; color: #6b21a8; }
.tag[data-categoria="alquiler"] { background: #ccfbf1; color: #0f766e; }
.tag[data-categoria="compra"] { background: #fce7f3; color: #9d174d; }
.tag[data-categoria="eventos"] { background: #fef9c3; color: #854d0e; }
.tag[data-categoria="tecnologia"] { background: #e0e7ff; color: #3730a3; }
.tag[data-categoria="hogar"] { background: #d1fae5; color: #065f46; }
.tag[data-categoria="ocio"] { background: #fee2e2; color: #991b1b; }
.tag[data-categoria="contactos"] { background: #cffafe; color: #155e75; }
.anuncio[data-categoria="general"] { border-left-color: #3b82f6; }
.anuncio[data-categoria="trabajo"] { border-left-color: #22c55e; }
.anuncio[data-categoria="venta"] { border-left-color: #f97316; }
.anuncio[data-categoria="servicios"] { border-left-color: #a855f7; }
.anuncio[data-categoria="alquiler"] { border-left-color: #14b8a6; }
.anuncio[data-categoria="compra"] { border-left-color: #ec4899; }
.anuncio[data-categoria="eventos"] { border-left-color: #eab308; }
.anuncio[data-categoria="tecnologia"] { border-left-color: #6366f1; }
.anuncio[data-categoria="hogar"] { border-left-color: #10b981; }
.anuncio[data-categoria="ocio"] { border-left-color: #ef4444; }
.anuncio[data-categoria="contactos"] { border-left-color: #06b6d4; }
.anuncio strong {
font-size: 1.02em;
font-weight: 600;
color: #111827;
}
.anuncio p {
margin: 6px 0 0 0;
color: #374151;
white-space: pre-wrap;
font-size: 0.92em;
line-height: 1.5;
}
/* Botones */
button {
font-family: inherit;
background: #111827;
color: #fff;
border: none;
border-radius: var(--radius-sm);
padding: 8px 14px;
cursor: pointer;
font-weight: 500;
font-size: 0.85em;
transition: background 0.15s ease, transform 0.1s ease;
}
button:hover {
background: #374151;
}
button:active {
transform: scale(0.98);
}
.meta button {
background: transparent;
color: #ef4444;
border: 1px solid #fecaca;
padding: 4px 10px;
font-size: 0.78em;
}
.meta button:hover {
background: #fef2f2;
}
/* Formularios */
form {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 20px;
margin-top: 16px;
box-shadow: var(--shadow);
}
.filtros {
display: flex;
gap: 12px;
flex-wrap: wrap;
align-items: flex-end;
}
.filtros > div {
flex: 1;
min-width: 160px;
}
.filtros > div:last-child {
flex: 0 0 auto;
display: flex;
align-items: center;
gap: 10px;
}
label {
display: block;
font-size: 0.85em;
font-weight: 500;
color: #4b5563;
margin-bottom: 6px;
}
input, textarea, select {
font-family: inherit;
width: 100%;
border: 1px solid #d1d5db;
border-radius: var(--radius-sm);
padding: 8px 10px;
background: #fff;
color: #1f2937;
font-size: 0.95em;
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
input:focus, textarea:focus, select:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59,130,246,0.1);
}
.filtros input, .filtros select {
margin-bottom: 0;
}
a {
color: #2563eb;
text-decoration: none;
font-weight: 500;
font-size: 0.9em;
}
a:hover {
text-decoration: underline;
}
hr {
border: none;
border-top: 1px solid var(--border);
margin: 28px 0;
}
.empty {
color: var(--text-muted);
font-size: 0.95em;
margin: 12px 0;
}
.footer {
font-size: 0.8em;
color: var(--text-muted);
text-align: center;
margin-top: 8px;
}
.back-link {
display: inline-block;
margin-bottom: 16px;
font-size: 0.85em;
}
</style>
</head>
<body>
<div class="container">
<h1>{% if modo == 'ciudad' %}<a href="/" style="color:inherit;text-decoration:none;">madcraig</a> <span style="color:var(--text-muted);font-weight:400;">// {{ city }}</span>{% else %}madcraig{% endif %}</h1>
{% if modo == 'inicio' %}
<p class="subtitle">Selecciona una ciudad para ver los anuncios.</p>
<div class="city-list">
{% for c in ciudades %}
<a class="city-card" href="/c/{{ c }}">
{{ c|replace('-', ' ') }}
<span>/c/{{ c }}</span>
</a>
{% endfor %}
</div>
{% else %}
<h2>Buscar anuncios</h2>
<form method="get" action="/c/{{ city }}" class="filtros">
<div>
<label for="q">Busqueda</label>
<input type="text" id="q" name="q" value="{{ busqueda_actual }}" placeholder="Titulo o contenido...">
</div>
<div>
<label for="categoria">Categoria</label>
<select id="categoria" name="categoria">
<option value="">Todas</option>
{% for cat in categorias %}
<option value="{{ cat }}" {% if categoria_actual == cat %}selected{% endif %}>{{ cat }}</option>
{% endfor %}
</select>
</div>
<div>
<button type="submit">Filtrar</button>
{% if categoria_actual or busqueda_actual %}
<a href="/c/{{ city }}">Limpiar</a>
{% endif %}
</div>
</form>
<hr>
<h2>Anuncios</h2>
{% for a in anuncios %}
<div class="anuncio" data-categoria="{{ a.categoria|lower }}" data-id="{{ a.id }}">
<div class="meta">
<span>
<span class="tag" data-categoria="{{ a.categoria|lower }}">{{ a.categoria }}</span>
<span>{{ a.fecha }} &middot; {{ a.contacto }}</span>
</span>
<form class="borrar-form" action="/borrar/{{ a.id }}" method="post" onsubmit="return confirm('Borrar este anuncio?');" style="display:none">
<input type="hidden" name="token" class="token-input">
<button type="submit">Borrar</button>
</form>
</div>
<strong>{{ a.titulo }}</strong>
<p>{{ a.contenido }}</p>
</div>
{% else %}
<p class="empty">No hay anuncios todavia.</p>
{% endfor %}
{% endif %}
<hr>
<p class="footer">madcraig — No rights reserved.</p>
</div>
<script>
(function() {
const KEY = 'mc_tokens';
function getTokens() {
try { return JSON.parse(localStorage.getItem(KEY)) || {}; } catch(e) { return {}; }
}
function setTokens(obj) {
localStorage.setItem(KEY, JSON.stringify(obj));
}
// Guardar token de un anuncio recién creado (viene por query string)
const params = new URLSearchParams(location.search);
const nuevoId = params.get('nuevo');
const nuevoToken = params.get('token');
if (nuevoId && nuevoToken) {
const t = getTokens();
t[nuevoId] = nuevoToken;
setTokens(t);
// Limpiar query string sin recargar
history.replaceState({}, '', location.pathname + location.hash);
}
// Mostrar botón borrar solo para anuncios propios
const tokens = getTokens();
document.querySelectorAll('.anuncio').forEach(function(card) {
const id = card.getAttribute('data-id');
const form = card.querySelector('.borrar-form');
const input = card.querySelector('.token-input');
if (form && tokens[id]) {
input.value = tokens[id];
form.style.display = 'inline';
}
});
})();
</script>
</body>
</html>