frist commit
This commit is contained in:
@@ -0,0 +1,422 @@
|
||||
<!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 }} · {{ 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>
|
||||
Reference in New Issue
Block a user