Venues and Waters - Basic implementation
I've added the model for the Venues, and implemented a basic system for displaying them in the venues and waters page.
This commit is contained in:
parent
b4686d5cec
commit
5fc0128c7c
@ -2,7 +2,14 @@
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Member, Team, Section
|
||||
from .models import Venue
|
||||
# from .models import Member, Team, Section
|
||||
|
||||
|
||||
@admin.register(Venue)
|
||||
class VenueAdmin(admin.ModelAdmin):
|
||||
"""Admin model for the Venue model."""
|
||||
|
||||
|
||||
|
||||
# @admin.register(Peg)
|
||||
@ -15,27 +22,27 @@ from .models import Member, Team, Section
|
||||
# search_fields = ("peg_number",)
|
||||
|
||||
|
||||
@admin.register(Member)
|
||||
class MemberAdmin(admin.ModelAdmin):
|
||||
"""Admin model for the Member model."""
|
||||
# @admin.register(Member)
|
||||
# class MemberAdmin(admin.ModelAdmin):
|
||||
# """Admin model for the Member model."""
|
||||
|
||||
list_display = ("first_name", "last_name", "team")
|
||||
search_fields = ("first_name", "last_name", "team")
|
||||
list_filter = ("team",)
|
||||
# list_display = ("first_name", "last_name", "team")
|
||||
# search_fields = ("first_name", "last_name", "team")
|
||||
# list_filter = ("team",)
|
||||
|
||||
|
||||
@admin.register(Team)
|
||||
class TeamAdmin(admin.ModelAdmin):
|
||||
"""Admin model for the Team model."""
|
||||
# @admin.register(Team)
|
||||
# class TeamAdmin(admin.ModelAdmin):
|
||||
# """Admin model for the Team model."""
|
||||
|
||||
# readonly_fields = ("id", "number",)
|
||||
list_display = ("id", "number",)
|
||||
search_fields = ("number",)
|
||||
# # readonly_fields = ("id", "number",)
|
||||
# list_display = ("id", "number",)
|
||||
# search_fields = ("number",)
|
||||
|
||||
|
||||
@admin.register(Section)
|
||||
class SectionAdmin(admin.ModelAdmin):
|
||||
"""Admin model for the Section model."""
|
||||
# @admin.register(Section)
|
||||
# class SectionAdmin(admin.ModelAdmin):
|
||||
# """Admin model for the Section model."""
|
||||
|
||||
list_display = ("id", "character",)
|
||||
search_fields = ("character",)
|
||||
# list_display = ("id", "character",)
|
||||
# search_fields = ("character",)
|
||||
|
@ -0,0 +1,45 @@
|
||||
# Generated by Django 4.1.5 on 2023-10-22 21:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mainapp', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Venue',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('description', models.TextField(blank=True)),
|
||||
('extra_notes', models.TextField(blank=True)),
|
||||
('venue_type', models.CharField(choices=[('FISHERY', 'Fishery'), ('CLUB', 'Club'), ('PRIVATE', 'Private')], max_length=50)),
|
||||
('phone_number', models.CharField(max_length=100)),
|
||||
('email_address', models.EmailField(max_length=254)),
|
||||
('website_url', models.URLField()),
|
||||
('street_address', models.CharField(max_length=100)),
|
||||
('city', models.CharField(max_length=255)),
|
||||
('provence', models.CharField(max_length=100)),
|
||||
('postal_code', models.CharField(max_length=20)),
|
||||
('country', models.CharField(max_length=100)),
|
||||
('latitude', models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True)),
|
||||
('longitude', models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True)),
|
||||
('twitter_url', models.URLField(blank=True)),
|
||||
('instagram_url', models.URLField(blank=True)),
|
||||
('facebook_url', models.URLField(blank=True)),
|
||||
],
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Member',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Section',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Team',
|
||||
),
|
||||
]
|
@ -4,194 +4,231 @@ from django.db import models
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
|
||||
class ReusableAutoField(models.PositiveIntegerField):
|
||||
"""A django auto field that can reuse deleted primary keys."""
|
||||
class Venue(models.Model):
|
||||
"""Represents a Venue and Waters."""
|
||||
|
||||
def get_next_available_id(self, model_cls, using=None):
|
||||
"""
|
||||
Returns the next available id for the given model class.
|
||||
"""
|
||||
all_ids = set(range(1, model_cls._default_manager.count()+1))
|
||||
used_ids = set(model_cls._default_manager.all().values_list('pk', flat=True))
|
||||
available_ids = all_ids - used_ids
|
||||
VENUE_TYPES = (
|
||||
("FISHERY", "Fishery"),
|
||||
("CLUB", "Club"),
|
||||
("PRIVATE", "Private")
|
||||
)
|
||||
|
||||
if available_ids:
|
||||
return min(available_ids)
|
||||
name = models.CharField(max_length=255)
|
||||
description = models.TextField(blank=True)
|
||||
extra_notes = models.TextField(blank=True)
|
||||
venue_type = models.CharField(choices=VENUE_TYPES, max_length=50)
|
||||
|
||||
if used_ids:
|
||||
return max(used_ids) + 1
|
||||
# Contact information
|
||||
phone_number = models.CharField(max_length=100)
|
||||
email_address = models.EmailField()
|
||||
website_url = models.URLField()
|
||||
|
||||
return 1
|
||||
# Location information
|
||||
street_address = models.CharField(max_length=100)
|
||||
city = models.CharField(max_length=255)
|
||||
provence = models.CharField(max_length=100)
|
||||
postal_code = models.CharField(max_length=20)
|
||||
country = models.CharField(max_length=100)
|
||||
latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
|
||||
longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
|
||||
|
||||
def get_default(self):
|
||||
"""Returns the default value for this field"""
|
||||
|
||||
return self.get_next_available_id(self.model)
|
||||
|
||||
|
||||
class SectionValidator:
|
||||
"""Validation class for the `section` field on the `member` model."""
|
||||
|
||||
def __init__(self, max_value="ZZZ"):
|
||||
self.max_value = max_value.upper()
|
||||
self.alphabet_size = ord("Z") - ord("A") + 1
|
||||
|
||||
def is_valid(self, section: str, team_sections: list[str]=None) -> bool:
|
||||
"""Returns boolean if the section passed is valid."""
|
||||
section = section.upper()
|
||||
|
||||
if not self._is_alphanumeric(section):
|
||||
return False
|
||||
|
||||
if not self._is_in_alphabet(section[0]):
|
||||
return False
|
||||
|
||||
if not self._is_in_range(section):
|
||||
return False
|
||||
|
||||
if team_sections:
|
||||
if not self._is_unique(section, team_sections):
|
||||
return False
|
||||
|
||||
if not self._is_not_adjacent(section, team_sections):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _is_alphanumeric(self, section: str) -> bool:
|
||||
"""Returns boolean if all characters in the passed string are alphanumerical."""
|
||||
return all(c.isalnum() for c in section)
|
||||
|
||||
def _is_in_alphabet(self, c) -> bool:
|
||||
"""Returns boolean if the passed character is alphabetical."""
|
||||
return "A" <= c <= "Z"
|
||||
|
||||
def _is_in_range(self, section) -> bool:
|
||||
"""Returns boolean if the passed section less or equal to the max value."""
|
||||
section_value = self._section_value(section)
|
||||
max_value = self._section_value(self.max_value)
|
||||
|
||||
return section_value <= max_value
|
||||
|
||||
def _is_unique(self, section, team_sections) -> bool:
|
||||
"""Returns boolean if the passed section is unique amongst `team_sections`."""
|
||||
return section not in team_sections
|
||||
|
||||
def _is_not_adjacent(self, section, team_sections) -> bool:
|
||||
"""Returns boolean if the passed section is not adjacent to any `team_sections`."""
|
||||
for team_section in team_sections:
|
||||
team_section_value = self._section_value(team_section)
|
||||
section_value = self._section_value(section)
|
||||
if abs(team_section_value - section_value) <= 1:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _section_value(self, section):
|
||||
"""Returns the value of the passed section."""
|
||||
n = len(section)
|
||||
value = sum((ord(c) - ord("A") + 1) * self.alphabet_size ** (n - i - 1) for i, c in enumerate(section))
|
||||
return value
|
||||
|
||||
|
||||
class SectionManager(models.Manager):
|
||||
|
||||
@staticmethod
|
||||
def get_max_section():
|
||||
max_section = None
|
||||
max_number = -1
|
||||
|
||||
# Iterate through all sections in the database
|
||||
for section in Section.objects.all():
|
||||
section_name = section.name
|
||||
section_number = 0
|
||||
|
||||
# Calculate the section number based on the section name
|
||||
for i, char in enumerate(section_name):
|
||||
section_number += (ord(char) - ord('A') + 1) * (26 ** (len(section_name) - i - 1))
|
||||
|
||||
# Check if this section has a higher number than the current maximum
|
||||
if section_number > max_number:
|
||||
max_number = section_number
|
||||
max_section = section_name
|
||||
|
||||
return max_section
|
||||
|
||||
@staticmethod
|
||||
def find_next_section(current_section):
|
||||
if not current_section:
|
||||
return 'A'
|
||||
|
||||
# Split current section name into a list of characters
|
||||
chars = list(current_section)
|
||||
|
||||
# Increment the last character
|
||||
chars[-1] = chr(ord(chars[-1]) + 1)
|
||||
|
||||
# Check if the last character is "Z", and carry over to the next character if necessary
|
||||
for i in range(len(chars) - 1, -1, -1):
|
||||
if chars[i] > 'Z':
|
||||
chars[i] = 'A'
|
||||
if i == 0:
|
||||
# If the first character needs to be incremented, add a new character "A"
|
||||
chars.insert(0, 'A')
|
||||
else:
|
||||
# Increment the previous character
|
||||
chars[i - 1] = chr(ord(chars[i - 1]) + 1)
|
||||
else:
|
||||
break
|
||||
|
||||
# Join the characters back into a string and return the result
|
||||
return ''.join(chars)
|
||||
|
||||
class Section(models.Model):
|
||||
"""Represents a fishing area. Members can be assigned to a section,
|
||||
but no 2 teammates can be in the same or adjacent section."""
|
||||
|
||||
character = models.CharField(max_length=3, unique=True, null=False)
|
||||
|
||||
objects = SectionManager()
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
self.character = self.character.upper()
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.character
|
||||
|
||||
|
||||
class Member(models.Model):
|
||||
"""Represents a member of a team"""
|
||||
|
||||
first_name = models.CharField(max_length=255)
|
||||
last_name = models.CharField(max_length=255)
|
||||
team = models.ForeignKey("Team", on_delete=models.SET_NULL, null=True, blank=True, related_name='members')
|
||||
peg_number = models.PositiveIntegerField(null=True, editable=True, unique=True)
|
||||
section = models.ForeignKey(to=Section, on_delete=models.SET_NULL, null=True, swappable=True, related_name='members')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# If the peg_number field is not set, we assign it the smallest
|
||||
# available positive integer, excluding any used values.
|
||||
if not self.peg_number:
|
||||
used_peg_numbers = Member.objects.exclude(id=self.id).exclude(peg_number=None).values_list("peg_number", flat=True)
|
||||
peg_numbers = set(range(1, Member.objects.count() + 1)) - set(used_peg_numbers)
|
||||
if peg_numbers:
|
||||
self.peg_number = min (peg_numbers)
|
||||
# Socials information
|
||||
twitter_url = models.URLField(blank=True)
|
||||
instagram_url = models.URLField(blank=True)
|
||||
facebook_url = models.URLField(blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.first_name} {self.last_name} (team {self.team.number}) [section {self.section.character}]"
|
||||
|
||||
@property
|
||||
def fullname(self) -> str:
|
||||
return f"{self.first_name} {self.last_name}"
|
||||
return self.name
|
||||
|
||||
|
||||
class Team(models.Model):
|
||||
"""Represents a team"""
|
||||
# class ReusableAutoField(models.PositiveIntegerField):
|
||||
# """A django auto field that can reuse deleted primary keys."""
|
||||
|
||||
number = models.PositiveIntegerField(unique=True, null=False, blank=False)
|
||||
# def get_next_available_id(self, model_cls, using=None):
|
||||
# """
|
||||
# Returns the next available id for the given model class.
|
||||
# """
|
||||
# all_ids = set(range(1, model_cls._default_manager.count()+1))
|
||||
# used_ids = set(model_cls._default_manager.all().values_list('pk', flat=True))
|
||||
# available_ids = all_ids - used_ids
|
||||
|
||||
def __str__(self):
|
||||
return f"Team {self.number}"
|
||||
# if available_ids:
|
||||
# return min(available_ids)
|
||||
|
||||
# if used_ids:
|
||||
# return max(used_ids) + 1
|
||||
|
||||
# return 1
|
||||
|
||||
# def get_default(self):
|
||||
# """Returns the default value for this field"""
|
||||
|
||||
# return self.get_next_available_id(self.model)
|
||||
|
||||
|
||||
# class SectionValidator:
|
||||
# """Validation class for the `section` field on the `member` model."""
|
||||
|
||||
# def __init__(self, max_value="ZZZ"):
|
||||
# self.max_value = max_value.upper()
|
||||
# self.alphabet_size = ord("Z") - ord("A") + 1
|
||||
|
||||
# def is_valid(self, section: str, team_sections: list[str]=None) -> bool:
|
||||
# """Returns boolean if the section passed is valid."""
|
||||
# section = section.upper()
|
||||
|
||||
# if not self._is_alphanumeric(section):
|
||||
# return False
|
||||
|
||||
# if not self._is_in_alphabet(section[0]):
|
||||
# return False
|
||||
|
||||
# if not self._is_in_range(section):
|
||||
# return False
|
||||
|
||||
# if team_sections:
|
||||
# if not self._is_unique(section, team_sections):
|
||||
# return False
|
||||
|
||||
# if not self._is_not_adjacent(section, team_sections):
|
||||
# return False
|
||||
|
||||
# return True
|
||||
|
||||
# def _is_alphanumeric(self, section: str) -> bool:
|
||||
# """Returns boolean if all characters in the passed string are alphanumerical."""
|
||||
# return all(c.isalnum() for c in section)
|
||||
|
||||
# def _is_in_alphabet(self, c) -> bool:
|
||||
# """Returns boolean if the passed character is alphabetical."""
|
||||
# return "A" <= c <= "Z"
|
||||
|
||||
# def _is_in_range(self, section) -> bool:
|
||||
# """Returns boolean if the passed section less or equal to the max value."""
|
||||
# section_value = self._section_value(section)
|
||||
# max_value = self._section_value(self.max_value)
|
||||
|
||||
# return section_value <= max_value
|
||||
|
||||
# def _is_unique(self, section, team_sections) -> bool:
|
||||
# """Returns boolean if the passed section is unique amongst `team_sections`."""
|
||||
# return section not in team_sections
|
||||
|
||||
# def _is_not_adjacent(self, section, team_sections) -> bool:
|
||||
# """Returns boolean if the passed section is not adjacent to any `team_sections`."""
|
||||
# for team_section in team_sections:
|
||||
# team_section_value = self._section_value(team_section)
|
||||
# section_value = self._section_value(section)
|
||||
# if abs(team_section_value - section_value) <= 1:
|
||||
# return False
|
||||
|
||||
# return True
|
||||
|
||||
# def _section_value(self, section):
|
||||
# """Returns the value of the passed section."""
|
||||
# n = len(section)
|
||||
# value = sum((ord(c) - ord("A") + 1) * self.alphabet_size ** (n - i - 1) for i, c in enumerate(section))
|
||||
# return value
|
||||
|
||||
|
||||
# class SectionManager(models.Manager):
|
||||
|
||||
# @staticmethod
|
||||
# def get_max_section():
|
||||
# max_section = None
|
||||
# max_number = -1
|
||||
|
||||
# # Iterate through all sections in the database
|
||||
# for section in Section.objects.all():
|
||||
# section_name = section.name
|
||||
# section_number = 0
|
||||
|
||||
# # Calculate the section number based on the section name
|
||||
# for i, char in enumerate(section_name):
|
||||
# section_number += (ord(char) - ord('A') + 1) * (26 ** (len(section_name) - i - 1))
|
||||
|
||||
# # Check if this section has a higher number than the current maximum
|
||||
# if section_number > max_number:
|
||||
# max_number = section_number
|
||||
# max_section = section_name
|
||||
|
||||
# return max_section
|
||||
|
||||
# @staticmethod
|
||||
# def find_next_section(current_section):
|
||||
# if not current_section:
|
||||
# return 'A'
|
||||
|
||||
# # Split current section name into a list of characters
|
||||
# chars = list(current_section)
|
||||
|
||||
# # Increment the last character
|
||||
# chars[-1] = chr(ord(chars[-1]) + 1)
|
||||
|
||||
# # Check if the last character is "Z", and carry over to the next character if necessary
|
||||
# for i in range(len(chars) - 1, -1, -1):
|
||||
# if chars[i] > 'Z':
|
||||
# chars[i] = 'A'
|
||||
# if i == 0:
|
||||
# # If the first character needs to be incremented, add a new character "A"
|
||||
# chars.insert(0, 'A')
|
||||
# else:
|
||||
# # Increment the previous character
|
||||
# chars[i - 1] = chr(ord(chars[i - 1]) + 1)
|
||||
# else:
|
||||
# break
|
||||
|
||||
# # Join the characters back into a string and return the result
|
||||
# return ''.join(chars)
|
||||
|
||||
# class Section(models.Model):
|
||||
# """Represents a fishing area. Members can be assigned to a section,
|
||||
# but no 2 teammates can be in the same or adjacent section."""
|
||||
|
||||
# character = models.CharField(max_length=3, unique=True, null=False)
|
||||
|
||||
# objects = SectionManager()
|
||||
|
||||
# def clean(self):
|
||||
# super().clean()
|
||||
# self.character = self.character.upper()
|
||||
|
||||
# def __str__(self) -> str:
|
||||
# return self.character
|
||||
|
||||
|
||||
# class Member(models.Model):
|
||||
# """Represents a member of a team"""
|
||||
|
||||
# first_name = models.CharField(max_length=255)
|
||||
# last_name = models.CharField(max_length=255)
|
||||
# team = models.ForeignKey("Team", on_delete=models.SET_NULL, null=True, blank=True, related_name='members')
|
||||
# peg_number = models.PositiveIntegerField(null=True, editable=True, unique=True)
|
||||
# section = models.ForeignKey(to=Section, on_delete=models.SET_NULL, null=True, swappable=True, related_name='members')
|
||||
|
||||
# def __init__(self, *args, **kwargs):
|
||||
# super().__init__(*args, **kwargs)
|
||||
|
||||
# # If the peg_number field is not set, we assign it the smallest
|
||||
# # available positive integer, excluding any used values.
|
||||
# if not self.peg_number:
|
||||
# used_peg_numbers = Member.objects.exclude(id=self.id).exclude(peg_number=None).values_list("peg_number", flat=True)
|
||||
# peg_numbers = set(range(1, Member.objects.count() + 1)) - set(used_peg_numbers)
|
||||
# if peg_numbers:
|
||||
# self.peg_number = min (peg_numbers)
|
||||
|
||||
# def __str__(self):
|
||||
# return f"{self.first_name} {self.last_name} (team {self.team.number}) [section {self.section.character}]"
|
||||
|
||||
# @property
|
||||
# def fullname(self) -> str:
|
||||
# return f"{self.first_name} {self.last_name}"
|
||||
|
||||
|
||||
# class Team(models.Model):
|
||||
# """Represents a team"""
|
||||
|
||||
# number = models.PositiveIntegerField(unique=True, null=False, blank=False)
|
||||
|
||||
# def __str__(self):
|
||||
# return f"Team {self.number}"
|
||||
|
181
src/mainapp/templates/venues.html
Normal file
181
src/mainapp/templates/venues.html
Normal file
@ -0,0 +1,181 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}
|
||||
Venues and Waters |
|
||||
{% endblock title %}
|
||||
|
||||
{% block style %}
|
||||
{% endblock style %}
|
||||
|
||||
{% block header_buttons %}
|
||||
<!-- <a href="/" class="btn btn-company d-flex align-items-center">
|
||||
<i class="bi bi-plus-lg"></i>
|
||||
<span class="ms-2">Add Venue and Waters</span>
|
||||
</a> -->
|
||||
{% endblock header_buttons %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="d-flex flex-column flex-grow-1">
|
||||
|
||||
<div class="m-4 mb-0 row">
|
||||
<div class="col-xl-4 col-md-8 mb-md-0 mb-4">
|
||||
<div class="input-group">
|
||||
<button type="button" data-bs-toggle="dropdown" class="btn btn-outline-company border-secondary-subtle">
|
||||
<i class="bi bi-sort-up"></i>
|
||||
</button>
|
||||
<input type="search" name="search" id="search" class="form-control border-secondary-subtle shadow-none" placeholder="Search Venues and Waters">
|
||||
<button type="button" class="btn btn-outline-company border-secondary-subtle rounded-end" id="searchButton"><i class="bi bi-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<div class="input-group d-flex justify-content-md-end">
|
||||
<button class="btn border-secondary-subtle btn-outline-company me-4 rounded-end" id="addVenue" data-bs-toggle="modal" data-bs-target="#venueModal" data-bs-toggle="tooltip" data-bs-title="Add Venues and Waters" data-bs-custom-class="light-tooltip">
|
||||
<i class="bi bi-plus-lg"></i>
|
||||
</button>
|
||||
<button class="btn border-secondary-subtle btn-outline-company rounded-start" id="ContractView" data-bs-toggle="tooltip" data-bs-title="List View" data-bs-custom-class="light-tooltip">
|
||||
<i class="bi bi-list-task"></i>
|
||||
</button>
|
||||
<button class="btn border-secondary-subtle btn-outline-company" id="expandView" data-bs-toggle="tooltip" data-bs-title="Grid View" data-bs-custom-class="light-tooltip">
|
||||
<i class="bi bi-grid"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mx-4 my-2">
|
||||
<div class="col-xl-7">
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="m-4 mt-0 row">
|
||||
|
||||
<!-- <div class="col-xl-3 col-lg-4 col-sm-6 mb-4">
|
||||
<div class="card w-100 h-100 fluid-hover-zoom shadow-sm md-shadow-on-hover overflow-hidden">
|
||||
<div class="card-badge-container">
|
||||
<div class="card-badge"></div>
|
||||
</div>
|
||||
<div class="card-body d-flex">
|
||||
<div class="d-flex flex-column">
|
||||
<h5 class="card-title text-company mb-0">Generic Location Name</h5>
|
||||
<div class="fw-bold mb-3">
|
||||
<p class="mb-0">Private</p>
|
||||
</div>
|
||||
<div class="text-body-secondary mb-3">
|
||||
<p class="mb-0">4 Branshaw Grove</p>
|
||||
<p class="mb-0">Newquay, Cornwall</p>
|
||||
<p class="mb-0">TR7 1LN</p>
|
||||
</div>
|
||||
<div class="text-body-secondary mb-3">
|
||||
<p class="mb-0">07796 081958</p>
|
||||
<p class="mb-0">branshaw.grove@mail.co.uk</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
{% for venue in venues %}
|
||||
<div class="col-xl-3 col-lg-4 col-sm-6 mb-4">
|
||||
<div class="card w-100 h-100 fluid-hover-zoom shadow-sm md-shadow-on-hover overflow-hidden">
|
||||
<div class="card-badge-container">
|
||||
<div class="card-badge"></div>
|
||||
</div>
|
||||
<div class="card-body d-flex">
|
||||
<div class="d-flex flex-column">
|
||||
<h5 class="card-title text-company mb-0">{{ venue.name }}</h5>
|
||||
<div class="fw-bold mb-3">
|
||||
<p class="mb-0">{{ venue.type }}</p>
|
||||
</div>
|
||||
<div class="text-body-secondary mb-3">
|
||||
<p class="mb-0">{{ venue.street_address }}</p>
|
||||
<p class="mb-0">{{ venue.city }}, {{ venue.provence }}</p>
|
||||
<p class="mb-0">{{ venue.postal_code }}</p>
|
||||
</div>
|
||||
<div class="text-body-secondary mb-3">
|
||||
<p class="mb-0">{{ venue.phone_number }}</p>
|
||||
<p class="mb-0">{{ venue.email_address }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="venueModal" class="modal fade">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content overflow-hidden">
|
||||
<div class="modal-body p-0">
|
||||
<div class="card-badge-container">
|
||||
<div class="card-badge"></div>
|
||||
</div>
|
||||
<div class="p-3">
|
||||
<h5 class="card-title text-company mb-4">New Venue and Waters</h5>
|
||||
<div class="row g-3 align-items-center mb-3">
|
||||
<div class="col-6">
|
||||
<label for="" class="form-label">Name</label>
|
||||
<input type="text" class="form-control">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label for="" class="form-label">Type</label>
|
||||
<select name="" id="" class="form-select">
|
||||
<option value="-1">placeholder</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="" class="form-label">Description</label>
|
||||
<textarea type="text" class="form-control" rows="3"></textarea>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="mb-3">
|
||||
<label for="" class="form-label">Street Address</label>
|
||||
<input type="text" class="form-control">
|
||||
</div>
|
||||
<div class="row g-3 align-items-center mb-3">
|
||||
<div class="col-6">
|
||||
<label for="" class="form-label">City/Town</label>
|
||||
<input type="text" class="form-control">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label for="" class="form-label">Provence</label>
|
||||
<input type="text" class="form-control">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label for="" class="form-label">Postal Code</label>
|
||||
<input type="text" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col-6">
|
||||
<label for="" class="form-label">Phone Number</label>
|
||||
<input type="tel" class="form-control">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label for="" class="form-label">Email Address</label>
|
||||
<input type="email" class="form-control">
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="" class="form-label">Website Address</label>
|
||||
<input type="url" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary px-3" data-dismiss="modal">Cancel</button>
|
||||
<button class="btn btn-company" data-dismiss="modal">Save New</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block scripts %}
|
||||
{% endblock scripts %}
|
@ -6,6 +6,7 @@ urlpatterns = [
|
||||
path('results/', views.results, name='results'),
|
||||
path('scoreboard/', views.scoreboard, name='scoreboard'),
|
||||
path('members/', views.teams, name='members'),
|
||||
path('venues/', views.venues, name='venues'),
|
||||
|
||||
# path('bulk-peg/', views.bulk_create_pegs, name='bulk-peg'),
|
||||
path('get-angler-data/', views.get_angler_page_data, name='get-angler-data'),
|
||||
|
@ -10,7 +10,8 @@ from django.views import View
|
||||
from django.views.decorators.http import require_GET, require_POST
|
||||
from django.http import HttpRequest
|
||||
|
||||
from .models import Team, Member, Section, SectionManager, ReusableAutoField, SectionValidator
|
||||
from .models import Venue
|
||||
# from .models import Team, Member, Section, SectionManager, ReusableAutoField, SectionValidator
|
||||
|
||||
|
||||
def index(request):
|
||||
@ -25,6 +26,13 @@ def scoreboard(request):
|
||||
def teams(request):
|
||||
return render(request, 'teams.html')
|
||||
|
||||
def venues(request):
|
||||
|
||||
venues = Venue.objects.all()
|
||||
context = {"venues": venues}
|
||||
|
||||
return render(request, 'venues.html', context)
|
||||
|
||||
# def bulk_create_pegs(request):
|
||||
# """Bulk create pegs"""
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
&.sidebar-enlarged {
|
||||
width: 280px !important;
|
||||
|
||||
.sidebar-collapse-button > i { transform: rotate(-180deg); }
|
||||
.sidebar-collapse-button { transform: rotate(-180deg); }
|
||||
|
||||
a.nav-link {
|
||||
text-align: start !important;
|
||||
@ -103,3 +103,23 @@
|
||||
|
||||
}
|
||||
|
||||
.card-badge-container {
|
||||
position: relative;
|
||||
|
||||
.card-badge {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
right: -30px;
|
||||
|
||||
background-color: #04385c;
|
||||
border-bottom: 30px;
|
||||
border-left: 30px;
|
||||
border-right: 30px;
|
||||
|
||||
transform: rotate(45deg);
|
||||
-webkit-transform: rotate(45deg);
|
||||
}
|
||||
}
|
@ -39,6 +39,12 @@
|
||||
<span class="sidebar-item-text">Anglers</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" data-bs-toggle="tooltip" data-bs-placement="right" data-bs-custom-class="light-tooltip" data-bs-title="Venues and Waters">
|
||||
<a class="nav-link py-3" href="/venues">
|
||||
<i class="bi bi-droplet-fill fs-4"></i>
|
||||
<span class="sidebar-item-text">Venues and Waters</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" data-bs-toggle="tooltip" data-bs-placement="right" data-bs-custom-class="light-tooltip" data-bs-title="Matches">
|
||||
<a class="nav-link py-3" href="#">
|
||||
<i class="bi bi-award-fill fs-4"></i>
|
||||
|
Loading…
x
Reference in New Issue
Block a user