initial commit

This commit is contained in:
Lucas Jensen
2024-05-01 09:19:01 -07:00
commit 5d67c0c2b2
117 changed files with 9917 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
.event-list-item {
background-color: transparent !important;
color: inherit;
border: 0.15rem solid var(--group-info-color);
}

View File

@@ -0,0 +1,70 @@
import { ListGroup } from "react-bootstrap";
import "./Event.css";
export class EventObj {
event_id: number;
location: string;
time: string; // ISO 8601 formatted date-time string
ticket_url?: string;
map_url?: string;
constructor(
event_id: number,
location: string,
time: string,
ticket_url?: string,
map_url?: string,
) {
this.event_id = event_id;
this.location = location;
this.time = time;
this.ticket_url = ticket_url;
this.map_url = map_url;
}
}
interface EventProps {
event: EventObj;
}
function Event(props: EventProps) {
const event = props.event;
const date = new Date(event.time);
const dateString = date.toLocaleDateString(undefined, {
month: "long",
day: "numeric",
weekday: "long",
});
const location = event.map_url ? (
<>
<a href={event.map_url} target="_blank" rel="noreferrer">
{event.location}
</a>
</>
) : (
event.location
);
const tickets = event.ticket_url ? (
<>
|{" "}
<a href={event.ticket_url} target="_blank" rel="noreferrer">
Tickets
</a>
</>
) : null;
const timeString = date.toLocaleTimeString(undefined, {
hour: "numeric",
minute: "2-digit",
hour12: true,
});
return (
<ListGroup.Item className="event-list-item">
<p>
{dateString} {timeString.toLowerCase()} | {location} {tickets}
</p>
</ListGroup.Item>
);
}
export default Event;

View File

@@ -0,0 +1,3 @@
.event-list {
margin-top: 2.5rem;
}

View File

@@ -0,0 +1,23 @@
import { ListGroup } from "react-bootstrap";
import Event, { EventObj } from "./Event/Event";
import "./Events.css";
interface EventsProps {
events: EventObj[];
}
function Events(props: EventsProps) {
const events = props.events;
return (
<>
<ListGroup className="event-list">
{events.map((event) => (
<Event key={event.event_id} event={event} />
))}
</ListGroup>
</>
);
}
export default Events;

View File

@@ -0,0 +1,90 @@
import { Card, Col, Container, Row } from "react-bootstrap";
import { EventSeriesObj } from "../SeriesList";
import Events from "../Events.tsx/Events";
import SeriesPoster from "./SeriesPoster";
import EditButton from "../../Buttons/EditButton/EditButton";
import DeleteButton from "../../Buttons/DeleteButton/DeleteButton";
import { useEffect, useState } from "react";
import { faEdit } from "@fortawesome/free-solid-svg-icons";
import EditModal from "../../EditModals/EditModal";
import EventForm from "../../Forms/Event/EventForm";
interface SeriesProps {
series: EventSeriesObj;
setSeriesList: React.Dispatch<React.SetStateAction<EventSeriesObj[]>>;
seriesList: EventSeriesObj[];
token: string;
}
function Series(props: SeriesProps) {
const [series, setSeries] = useState<EventSeriesObj>(props.series);
const [modalShow, setModalShow] = useState(false);
useEffect(() => {
setSeries(props.series);
}, [props.series]);
const EditableSeries = (
<Container>
<Row>
<Col>
<Container className="text-center">
<EditButton setModalShow={setModalShow} faIcon={faEdit} />
<EditModal
show={modalShow}
onHide={() => setModalShow(false)}
title="Edit Concert Series"
entity={props.series}
form={
<EventForm
setModalShow={setModalShow}
setSeriesList={props.setSeriesList}
seriesList={props.seriesList}
series={series}
isNewSeries={false}
setSeries={setSeries}
token={props.token}
/>
}
/>
</Container>
</Col>
<Col>
<Container className="text-center">
<DeleteButton
series={series}
setSeriesList={props.setSeriesList}
seriesList={props.seriesList}
token={props.token}
/>
</Container>
</Col>
</Row>
</Container>
);
return (
<Row id={`series-${series.series_id}-row`}>
<Col>
<SeriesPoster
series={series}
setSeries={setSeries}
token={props.token}
/>
</Col>
<Col>
<Container>
<Card>
<h4>{series.name}</h4>
{props.token && EditableSeries}
<p>{series.description}</p>
<Events events={series.events} />
</Card>
</Container>
</Col>
</Row>
);
}
export default Series;

View File

@@ -0,0 +1,57 @@
import { Container, Image } from "react-bootstrap";
import { EventSeriesObj } from "../SeriesList";
import cld from "../../Cld/CloudinaryConfig";
import { useState } from "react";
import EditButton from "../../Buttons/EditButton/EditButton";
import { faUpload } from "@fortawesome/free-solid-svg-icons";
import EditModal from "../../EditModals/EditModal";
import PosterUploadForm from "../../Forms/PosterUpload/PosterUploadForm";
interface SeriesPosterProps {
series: EventSeriesObj;
setSeries: React.Dispatch<React.SetStateAction<EventSeriesObj>>;
token: string;
}
function SeriesPoster(props: SeriesPosterProps) {
const series = props.series;
const imgUrl = cld.image(series.poster_id).toURL();
const imgSrc = imgUrl ? imgUrl : undefined;
const [modalShow, setModalShow] = useState(false);
const EditablePoster = (
<Container className="">
<EditButton
setModalShow={setModalShow}
faIcon={faUpload}
actionName=" Poster"
/>
<EditModal
show={modalShow}
onHide={() => setModalShow(false)}
title="Edit Poster"
entity={props.series}
form={
<PosterUploadForm
series={series}
currentPoster={imgSrc}
setModalShow={setModalShow}
setSeries={props.setSeries}
token={props.token}
/>
}
/>
</Container>
);
return (
<Container>
{series.poster_id ? (
<Image src={imgSrc} alt={series.name} className="img-fluid" />
) : null}
{props.token && EditablePoster}
</Container>
);
}
export default SeriesPoster;

View File

@@ -0,0 +1,19 @@
.events-title {
color: var(--group-info-color);
margin-bottom: 6rem;
}
hr {
border: 0;
height: 4px; /* This controls the thickness of the divider */
background: rgba(124, 1, 68); /* This sets the color of the divider */
margin-top: 10rem;
margin-bottom: 6rem;
opacity: 0.9;
}
#events {
padding-top: 70px;
margin-top: -70px;
margin-bottom: 400px;
}

View File

@@ -0,0 +1,106 @@
import { Col, Container } from "react-bootstrap";
import { EventObj } from "./Events.tsx/Event/Event";
import Series from "./Series/Series";
import "./SeriesList.css";
import EditButton from "../Buttons/EditButton/EditButton";
import { useState } from "react";
import { faAdd } from "@fortawesome/free-solid-svg-icons";
import EditModal from "../EditModals/EditModal";
import EventForm from "../Forms/Event/EventForm";
export class EventSeriesObj {
series_id: number;
name: string;
description: string;
events: EventObj[];
poster_id?: string; // Cloudinary public ID
constructor(
series_id: number,
name: string,
description: string,
events: EventObj[],
poster_id?: string,
) {
this.series_id = series_id;
this.name = name;
this.description = description;
this.events = events;
this.poster_id = poster_id;
}
}
interface SeriesListProps {
seriesList: EventSeriesObj[];
setSeriesList: React.Dispatch<React.SetStateAction<EventSeriesObj[]>>;
token: string;
}
function SeriesList(props: SeriesListProps) {
const seriesList = props.seriesList;
const [modalShow, setModalShow] = useState(false);
const AddableSeries = (
<Container className="text-end">
<EditButton
setModalShow={setModalShow}
faIcon={faAdd}
actionName=" Event"
/>
<EditModal
show={modalShow}
onHide={() => setModalShow(false)}
title="Add Concert Series"
form={
<EventForm
setModalShow={setModalShow}
setSeriesList={props.setSeriesList}
seriesList={props.seriesList}
isNewSeries={true}
token={props.token}
/>
}
/>
</Container>
);
if (seriesList.length === 0) {
return (
<section id="events">
<Col>
<Container>
<h3 className="display-3 text-end events-title">Upcoming Events</h3>
{props.token && AddableSeries}
</Container>
<Container>
<h3 className="display-4 text-center events-title">Stay tuned!</h3>
</Container>
</Col>
</section>
);
}
return (
<section id="events">
<Col>
<Container>
<h3 className="display-3 text-end events-title">Upcoming Events</h3>
{props.token && AddableSeries}
</Container>
{seriesList.map((series, idx) => (
<Container key={series.series_id}>
<Series
series={series}
setSeriesList={props.setSeriesList}
seriesList={props.seriesList}
token={props.token}
/>
{idx < seriesList.length - 1 && <hr className="series-divider" />}
</Container>
))}
</Col>
</section>
);
}
export default SeriesList;