starting admin work

This commit is contained in:
2025-06-28 02:55:17 -05:00
parent 13d56066ab
commit adbb3250ad
11 changed files with 205 additions and 89 deletions

View File

@ -26,6 +26,8 @@ services:
timeout: 3s
retries: 3
start_period: 30s
networks:
- appnet
db:
image: mysql:8
@ -39,12 +41,18 @@ services:
- "42000:3306"
volumes:
- ./mysql_data:/var/lib/mysql
entrypoint: ["sh", "-c", "mkdir -p /var/lib/mysql && chown -R 1000:998 /var/lib/mysql && chmod -R 770 /var/lib/mysql && exec docker-entrypoint.sh mysqld"]
entrypoint: >
sh -c "mkdir -p /var/lib/mysql &&
chown -R 1000:998 /var/lib/mysql &&
chmod -R 770 /var/lib/mysql &&
exec docker-entrypoint.sh mysqld"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
networks:
- appnet
adminer:
image: adminer
@ -55,6 +63,8 @@ services:
- ADMINER_DEFAULT_SERVER=db
depends_on:
- db
networks:
- appnet
neo4j:
image: neo4j:5.18
@ -66,6 +76,12 @@ services:
- NEO4J_AUTH=neo4j/your_secure_password
volumes:
- neo4j_data:/data
networks:
- appnet
volumes:
neo4j_data:
networks:
appnet:
driver: bridge

View File

@ -0,0 +1,28 @@
"""auto-migrate
Revision ID: 19e2a1b15b5e
Revises: f00a9585a348
Create Date: 2025-06-27 22:59:54.162560
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '19e2a1b15b5e'
down_revision = 'f00a9585a348'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###

View File

@ -0,0 +1,28 @@
"""auto-migrate
Revision ID: 85b7ca21ec19
Revises: 8c1e8db7b3cb
Create Date: 2025-06-27 23:34:04.669553
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '85b7ca21ec19'
down_revision = '8c1e8db7b3cb'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###

View File

@ -0,0 +1,28 @@
"""auto-migrate
Revision ID: 8c1e8db7b3cb
Revises: 19e2a1b15b5e
Create Date: 2025-06-27 23:21:19.031362
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '8c1e8db7b3cb'
down_revision = '19e2a1b15b5e'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###

BIN
nip.zip

Binary file not shown.

View File

@ -16,107 +16,123 @@
<h2 class="mb-4">View Entries</h2>
{# ── Import / Export, Stats, Filters & View Toggle ─────────────────────── #}
<div class="mb-3 d-flex flex-wrap justify-content-between align-items-center">
<!-- Left: import/export & stats toggle -->
<div class="d-flex align-items-center mb-2">
<button class="btn btn-primary me-2" data-bs-toggle="modal" data-bs-target="#importModal">
Import CSV
</button>
<a href="{{ url_for('utility.export_data') }}" class="btn btn-secondary me-2">
Export My Data
</a>
<button
class="btn btn-secondary me-2 d-inline-block d-md-none"
data-bs-toggle="modal"
data-bs-target="#statsModal">
Stats
</button>
<button
class="btn btn-secondary me-2 d-none d-md-inline-block"
data-bs-toggle="collapse"
data-bs-target="#statsBox"
aria-expanded="false"
aria-controls="statsBox"
id="statsToggle">
Stats <i class="bi bi-chevron-down"></i>
</button>
{# ── Import / Export / Stats & Filters / View Toggle ───────────────────── #}
<div class="mb-3 d-flex flex-wrap justify-content-between align-items-start">
{# LEFT: Import/Export + Stats toggles #}
<div class="btn-toolbar mb-2" role="toolbar">
<div class="btn-group me-2" role="group">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#importModal">
Import CSV
</button>
</div>
<div class="btn-group me-2" role="group">
<a href="{{ url_for('utility.export_data') }}" class="btn btn-secondary">
Export My Data
</a>
<button
class="btn btn-secondary d-inline-block d-md-none"
data-bs-toggle="modal"
data-bs-target="#statsModal">
Stats
</button>
<button
class="btn btn-secondary d-none d-md-inline-block"
data-bs-toggle="collapse"
data-bs-target="#statsBox"
aria-expanded="false"
aria-controls="statsBox"
id="statsToggle">
Stats <i class="bi bi-chevron-down"></i>
</button>
</div>
</div>
<!-- Right: filter form + view toggle -->
{# RIGHT: filter form + view toggle #}
<form
method="get"
action="{{ url_for('plant.index') }}"
class="d-flex flex-wrap align-items-center mb-2"
class="row gx-2 gy-2 align-items-center mb-2"
>
<div class="input-group me-2" style="min-width:200px;">
<span class="input-group-text">Search</span>
<input
type="search"
name="q"
value="{{ q }}"
class="form-control"
placeholder="by name…"
/>
<div class="col-auto">
<div class="input-group">
<span class="input-group-text">Search</span>
<input
type="search"
name="q"
value="{{ q }}"
class="form-control"
placeholder="by name…"
/>
</div>
</div>
<select
name="type"
class="form-select me-2"
style="min-width:140px;"
onchange="this.form.submit()"
>
<option value="">All Types</option>
{% for t in plant_types %}
<option
value="{{ t|lower }}"
{% if t|lower == type_filter %}selected{% endif %}
>{{ t }}</option>
{% endfor %}
</select>
<div class="col-auto">
<select
name="type"
class="form-select"
style="min-width:140px;"
onchange="this.form.submit()"
>
<option value="">All Types</option>
{% for t in plant_types %}
<option
value="{{ t|lower }}"
{% if t|lower == type_filter %}selected{% endif %}
>{{ t }}</option>
{% endfor %}
</select>
</div>
<select
name="per_page"
class="form-select me-2"
style="min-width:140px;"
onchange="this.form.submit()"
>
{% for size in [6,12,18,24] %}
<option value="{{ size }}" {% if per_page == size %}selected{% endif %}>
{{ size }} per page
</option>
{% endfor %}
</select>
<div class="col-auto">
<select
name="per_page"
class="form-select"
style="min-width:140px;"
onchange="this.form.submit()"
>
{% for size in [6,12,18,24] %}
<option value="{{ size }}" {% if per_page == size %}selected{% endif %}>
{{ size }} per page
</option>
{% endfor %}
</select>
</div>
{# keep the current view so Apply doesnt reset it #}
{# preserve current view so Apply doesnt reset it #}
<input type="hidden" name="view" value="{{ view_mode }}" />
<button type="submit" class="btn btn-primary me-2">Apply</button>
<div class="col-auto">
<button type="submit" class="btn btn-primary">Apply</button>
</div>
<div class="btn-group" role="group" aria-label="View mode">
<a
href="{{ url_for('plant.index',
page=pagination.page,
per_page=per_page,
q=q,
type=type_filter,
view='grid'
) }}"
class="btn btn-outline-secondary {% if view_mode=='grid' %}active{% endif %}"
title="Card View"
><i class="bi bi-grid-3x3-gap-fill"></i></a>
<a
href="{{ url_for('plant.index',
page=pagination.page,
per_page=per_page,
q=q,
type=type_filter,
view='list'
) }}"
class="btn btn-outline-secondary {% if view_mode=='list' %}active{% endif %}"
title="List View"
><i class="bi bi-list-ul"></i></a>
<div class="col-auto">
<div class="btn-group" role="group" aria-label="View mode">
<a
href="{{ url_for('plant.index',
page=pagination.page,
per_page=per_page,
q=q,
type=type_filter,
view='grid') }}"
class="btn btn-outline-secondary {% if view_mode=='grid' %}active{% endif %}"
title="Card View"
>
<i class="bi bi-grid-3x3-gap-fill"></i>
</a>
<a
href="{{ url_for('plant.index',
page=pagination.page,
per_page=per_page,
q=q,
type=type_filter,
view='list') }}"
class="btn btn-outline-secondary {% if view_mode=='list' %}active{% endif %}"
title="List View"
>
<i class="bi bi-list-ul"></i>
</a>
</div>
</div>
</form>
</div>