Compare commits
No commits in common. "ai-dev" and "master" have entirely different histories.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,9 +1,3 @@
|
||||
from django.contrib import admin
|
||||
from .models import Member
|
||||
|
||||
@admin.register(Member)
|
||||
class MemberAdmin(admin.ModelAdmin):
|
||||
list_display = ('full_name', 'email', 'role', 'status', 'join_date')
|
||||
list_filter = ('role', 'status', 'join_date')
|
||||
search_fields = ('full_name', 'email', 'notes')
|
||||
list_per_page = 25
|
||||
# Register your models here.
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
# Generated by Django 5.2.7 on 2026-02-24 00:57
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Member',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('full_name', models.CharField(max_length=255)),
|
||||
('email', models.EmailField(max_length=254, unique=True)),
|
||||
('phone', models.CharField(blank=True, max_length=20, null=True)),
|
||||
('role', models.CharField(choices=[('STAFF', 'Staff'), ('BOARD', 'Board Member'), ('YOUTH_VOLUNTEER', 'Youth Volunteer')], default='STAFF', max_length=20)),
|
||||
('status', models.CharField(choices=[('ACTIVE', 'Active'), ('INACTIVE', 'Inactive')], default='ACTIVE', max_length=10)),
|
||||
('join_date', models.DateField(auto_now_add=True)),
|
||||
('notes', models.TextField(blank=True, null=True)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-join_date'],
|
||||
},
|
||||
),
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
@ -1,27 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
class Member(models.Model):
|
||||
ROLE_CHOICES = [
|
||||
('STAFF', 'Staff'),
|
||||
('BOARD', 'Board Member'),
|
||||
('YOUTH_VOLUNTEER', 'Youth Volunteer'),
|
||||
]
|
||||
|
||||
STATUS_CHOICES = [
|
||||
('ACTIVE', 'Active'),
|
||||
('INACTIVE', 'Inactive'),
|
||||
]
|
||||
|
||||
full_name = models.CharField(max_length=255)
|
||||
email = models.EmailField(unique=True)
|
||||
phone = models.CharField(max_length=20, blank=True, null=True)
|
||||
role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='STAFF')
|
||||
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='ACTIVE')
|
||||
join_date = models.DateField(auto_now_add=True)
|
||||
notes = models.TextField(blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.full_name} ({self.get_role_display()})"
|
||||
|
||||
class Meta:
|
||||
ordering = ['-join_date']
|
||||
# Create your models here.
|
||||
|
||||
@ -1,53 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}TeamLink{% endblock %}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
{% load static %}
|
||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
|
||||
{% block head %}{% endblock %}
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}Knowledge Base{% endblock %}</title>
|
||||
{% if project_description %}
|
||||
<meta name="description" content="{{ project_description }}">
|
||||
<meta property="og:description" content="{{ project_description }}">
|
||||
<meta property="twitter:description" content="{{ project_description }}">
|
||||
{% endif %}
|
||||
{% if project_image_url %}
|
||||
<meta property="og:image" content="{{ project_image_url }}">
|
||||
<meta property="twitter:image" content="{{ project_image_url }}">
|
||||
{% endif %}
|
||||
{% load static %}
|
||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg bg-white shadow-sm sticky-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand fw-bold" href="{% url 'home' %}">
|
||||
<i class="bi bi-people-fill text-emerald"></i> TeamLink
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'home' %}">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'member_list' %}">Directory</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link btn btn-outline-primary ms-lg-3 px-3 py-1" href="/admin/">
|
||||
Admin Login
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main>
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer class="py-4 mt-5 bg-white border-top">
|
||||
<div class="container text-center">
|
||||
<p class="text-muted mb-0">© {{ current_time.year }} TeamLink - Internal Member Management</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
{% block content %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
|
||||
@ -1,86 +1,145 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}TeamLink - Organization Member Database{% endblock %}
|
||||
{% block title %}{{ project_name }}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<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=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M-10 10L110 10M10 -10L10 110' stroke-width='1' stroke='rgba(255,255,255,0.05)'/></svg>");
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@keyframes bg-pan {
|
||||
0% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2.5rem 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2.2rem, 3vw + 1.2rem, 3.2rem);
|
||||
font-weight: 700;
|
||||
margin: 0 0 1.2rem;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.loader {
|
||||
margin: 1.5rem auto;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.runtime code {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
padding: 0.15rem 0.45rem;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.75;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="hero-section text-center text-lg-start">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-7">
|
||||
<h1 class="display-4 fw-bold">Connecting our <br><span class="text-emerald">People and Purpose</span></h1>
|
||||
<p class="lead text-white-50 mb-4">The centralized hub for managing Staff, Board Members, and Youth Volunteers. Secure, searchable, and always accessible.</p>
|
||||
<div class="d-flex gap-3 justify-content-center justify-content-lg-start">
|
||||
<a href="{% url 'member_list' %}" class="btn btn-primary btn-lg">Browse Directory</a>
|
||||
<a href="/admin/core/member/add/" class="btn btn-outline-light btn-lg">Add Member</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-5 d-none d-lg-block">
|
||||
<div class="p-4 bg-white rounded-4 shadow-lg text-dark">
|
||||
<h4 class="mb-4">Quick Stats</h4>
|
||||
<div class="row g-3">
|
||||
<div class="col-6">
|
||||
<div class="stat-card border rounded-3 p-3">
|
||||
<span class="number h2 mb-0">{{ stats.total }}</span>
|
||||
<span class="label small">Total Members</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="stat-card border rounded-3 p-3">
|
||||
<span class="number h2 mb-0">{{ stats.staff }}</span>
|
||||
<span class="label small">Staff</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="stat-card border rounded-3 p-3">
|
||||
<span class="number h2 mb-0">{{ stats.board }}</span>
|
||||
<span class="label small">Board</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="stat-card border rounded-3 p-3">
|
||||
<span class="number h2 mb-0">{{ stats.volunteers }}</span>
|
||||
<span class="label small">Volunteers</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your app…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-5">
|
||||
<div class="container">
|
||||
<div class="row text-center mb-5">
|
||||
<div class="col-lg-8 mx-auto">
|
||||
<h2 class="h3 mb-3">One Secure Location for All Team Records</h2>
|
||||
<p class="text-muted">Effortlessly manage your organization's core contacts with advanced search, role-based filtering, and administrative oversight.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100 p-4">
|
||||
<div class="icon h3 text-primary mb-3"><i class="bi bi-shield-lock-fill"></i></div>
|
||||
<h5>Secure Management</h5>
|
||||
<p class="text-muted small">Admin-only data entry ensures your sensitive member data is protected and managed by authorized staff only.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100 p-4">
|
||||
<div class="icon h3 text-primary mb-3"><i class="bi bi-search"></i></div>
|
||||
<h5>Quick Discovery</h5>
|
||||
<p class="text-muted small">Instantly find any staff member, board representative, or volunteer using our powerful search and filter tools.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100 p-4">
|
||||
<div class="icon h3 text-primary mb-3"><i class="bi bi-file-earmark-spreadsheet-fill"></i></div>
|
||||
<h5>Reporting Ready</h5>
|
||||
<p class="text-muted small">Our structured data model makes it easy to export and report on your organization's human resources at any time.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
<p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will refresh automatically as the plan is implemented.</p>
|
||||
<p class="runtime">
|
||||
Runtime: Django <code>{{ django_version }}</code> · Python <code>{{ python_version }}</code>
|
||||
— UTC <code>{{ current_time|date:"Y-m-d H:i:s" }}</code>
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC)
|
||||
</footer>
|
||||
{% endblock %}
|
||||
@ -1,8 +1,7 @@
|
||||
from django.urls import path
|
||||
from .views import home, member_list, member_detail
|
||||
|
||||
from .views import home
|
||||
|
||||
urlpatterns = [
|
||||
path("", home, name="home"),
|
||||
path("members/", member_list, name="member_list"),
|
||||
path("members/<int:pk>/", member_detail, name="member_detail"),
|
||||
]
|
||||
]
|
||||
|
||||
@ -2,53 +2,24 @@ import os
|
||||
import platform
|
||||
|
||||
from django import get_version as django_version
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.shortcuts import render
|
||||
from django.utils import timezone
|
||||
from .models import Member
|
||||
|
||||
|
||||
def home(request):
|
||||
"""Render the landing screen with directory stats."""
|
||||
"""Render the landing screen with loader and environment details."""
|
||||
host_name = request.get_host().lower()
|
||||
agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
|
||||
|
||||
stats = {
|
||||
'total': Member.objects.count(),
|
||||
'staff': Member.objects.filter(role='STAFF').count(),
|
||||
'board': Member.objects.filter(role='BOARD').count(),
|
||||
'volunteers': Member.objects.filter(role='YOUTH_VOLUNTEER').count(),
|
||||
}
|
||||
now = timezone.now()
|
||||
|
||||
context = {
|
||||
"project_name": "TeamLink",
|
||||
"project_name": "New Style",
|
||||
"agent_brand": agent_brand,
|
||||
"django_version": django_version(),
|
||||
"python_version": platform.python_version(),
|
||||
"current_time": timezone.now(),
|
||||
"stats": stats,
|
||||
"current_time": now,
|
||||
"host_name": host_name,
|
||||
"project_description": os.getenv("PROJECT_DESCRIPTION", ""),
|
||||
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
|
||||
}
|
||||
return render(request, "core/index.html", context)
|
||||
|
||||
def member_list(request):
|
||||
"""View to list all members with filtering and search."""
|
||||
query = request.GET.get('q')
|
||||
role_filter = request.GET.get('role')
|
||||
|
||||
members = Member.objects.all()
|
||||
|
||||
if query:
|
||||
members = members.filter(full_name__icontains=query) | members.filter(email__icontains=query)
|
||||
|
||||
if role_filter:
|
||||
members = members.filter(role=role_filter)
|
||||
|
||||
context = {
|
||||
"members": members,
|
||||
"query": query,
|
||||
"role_filter": role_filter,
|
||||
}
|
||||
return render(request, "core/member_list.html", context)
|
||||
|
||||
def member_detail(request, pk):
|
||||
"""View for individual member details."""
|
||||
member = get_object_or_404(Member, pk=pk)
|
||||
return render(request, "core/member_detail.html", {"member": member})
|
||||
@ -1,100 +1,4 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Outfit:wght@600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--primary-color: #10b981; /* Vibrant Emerald */
|
||||
--secondary-color: #1a1d23; /* Deep Charcoal */
|
||||
--bg-color: #f8fafc; /* Soft Slate */
|
||||
--text-color: #334155;
|
||||
--heading-font: 'Outfit', sans-serif;
|
||||
--body-font: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
/* Custom styles for the application */
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
font-family: var(--body-font);
|
||||
line-height: 1.6;
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: var(--heading-font);
|
||||
color: var(--secondary-color);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.hero-section {
|
||||
background: linear-gradient(135deg, var(--secondary-color) 0%, #334155 100%);
|
||||
color: white;
|
||||
padding: 100px 0;
|
||||
margin-bottom: 50px;
|
||||
border-radius: 0 0 30px 30px;
|
||||
}
|
||||
|
||||
.hero-section h1 {
|
||||
color: white;
|
||||
font-size: 3.5rem;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: none;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
border-radius: 8px;
|
||||
padding: 10px 25px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #059669;
|
||||
border-color: #059669;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-card .number {
|
||||
font-size: 2.5rem;
|
||||
color: var(--primary-color);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stat-card .label {
|
||||
font-weight: 500;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
font-weight: 500;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-family: var(--heading-font);
|
||||
font-size: 1.5rem;
|
||||
color: var(--secondary-color) !important;
|
||||
}
|
||||
|
||||
.badge-staff { background-color: #dcfce7; color: #166534; }
|
||||
.badge-board { background-color: #dbeafe; color: #1e40af; }
|
||||
.badge-volunteer { background-color: #fef9c3; color: #854d0e; }
|
||||
|
||||
.search-bar {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user