diff --git a/components/admin/ticket.tsx b/components/admin/ticket.tsx
index 14c89b84..be26f4f3 100644
--- a/components/admin/ticket.tsx
+++ b/components/admin/ticket.tsx
@@ -7,6 +7,7 @@ import Link from "next/link";
import NameChangeModal from './modals/nameChangeModal';
import TicketTransferModal from './modals/ticketTransferModal';
import { fetcher } from "@lib/fetchers";
+import { guaranteeTimestampFromDate } from "@lib/useful";
const accessToThings = (access:number[],) => {
let products = []
@@ -52,7 +53,7 @@ export default function TicketView({ticket_number, email}: {ticket_number: strin
if(ticket) {
const ticketUsage = ticket.active ? ticket.ticket_used ? `Used @ ${format(ticket.ticket_used,' eee')}` : "Active & Unused" : "Deactivated"
- const purchaseData = fromUnixTime(ticket.purchase_date)
+ const purchaseDate = fromUnixTime(guaranteeTimestampFromDate(ticket.purchase_date) / 1000)
const purchasedThings = ticket.line_items ? ticket.line_items.map((item) => {return `${item.description} £${item.amount_total / 100}`}): []
return ( data &&
@@ -104,7 +105,7 @@ export default function TicketView({ticket_number, email}: {ticket_number: strin
Purchase
-
+
{ ticket.promo_code ?
: null }
diff --git a/components/admin/ticketList.tsx b/components/admin/ticketList.tsx
index cdcc53e1..e312488f 100644
--- a/components/admin/ticketList.tsx
+++ b/components/admin/ticketList.tsx
@@ -1,4 +1,5 @@
'use client'
+
import { useSearchParams, useRouter, usePathname} from 'next/navigation'
import { useState} from 'react';
import { ChevronDownIcon, ChevronUpIcon, ChevronUpDownIcon } from '@heroicons/react/24/solid'
@@ -10,7 +11,6 @@ import { TicketRow } from './lists/ticketRow';
import { fetcher } from "@lib/fetchers";
export default function TicketList() {
-
const searchParams = useSearchParams()
const pathname = usePathname()
const router = useRouter()
@@ -74,6 +74,8 @@ export default function TicketList() {
const headerContainerClassNames = "flex justify-between"
const labelClassNames = "py-3.5 pl-4 block"
+
+
if(isLoading) { return
Loading...
}
// else if (isValidating) { return
Validating...plz
}
else if (error) { return
Error on fetch {JSON.stringify(error)}
}
@@ -83,6 +85,7 @@ export default function TicketList() {
else {
const sortedAttendees = attendees.sort((a, b) => {
+ // console.log("Sorting",sortByField,a,b)
if (sortByDirection === 'desc') {
return (0 - (a[sortByField] > b[sortByField] ? 1 : -1))
} else {
@@ -118,7 +121,7 @@ export default function TicketList() {
Name
& Details
- & Email
+ & Email
diff --git a/components/admin/userList.tsx b/components/admin/userList.tsx
index f738ef93..67689f78 100644
--- a/components/admin/userList.tsx
+++ b/components/admin/userList.tsx
@@ -33,6 +33,7 @@ export default function UserList({loggedInUser}) {
console.log("person:",person)
const admin = person.metadata?.public?.admin;
const title = person.metadata?.public?.title;
+ const roles = person.metadata?.public?.roles || [];
const isMe = person.id === loggedInUser
return (
Title
{title}
- Role
+ User Type
+ px-3 pb-1 pt-1 text-xs font-medium ml-3 ring-1 ring-inset `}>
{admin ? "Admin" : "User"}
+ Roles
+
+ {roles.join(", ")}
+
diff --git a/components/layout/layout.tsx b/components/layout/layout.tsx
index babef881..5343895a 100644
--- a/components/layout/layout.tsx
+++ b/components/layout/layout.tsx
@@ -16,7 +16,7 @@ export default async function Layout({ children, rawPageData }: LayoutProps) {
return (
-
+
- {/* */}
- {
- router.push(`${origPath}?draft=yup`)
- }}>
+
{" "}
@@ -81,14 +55,8 @@ export default function Header() {
-
+
+
-
+
);
}
-
-// 'use client'
-
-// import { useState } from 'react'
-// import { Dialog, DialogPanel } from '@headlessui/react'
-// import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'
-
-// const navigation = [
-// { name: 'Product', href: '#' },
-// { name: 'Features', href: '#' },
-// { name: 'Marketplace', href: '#' },
-// { name: 'Company', href: '#' },
-// ]
-
-// export default function Example() {
-// const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
-
-// return (
-//
-// )
-// }
diff --git a/components/nav/logo.tsx b/components/nav/logo.tsx
new file mode 100644
index 00000000..72841c86
--- /dev/null
+++ b/components/nav/logo.tsx
@@ -0,0 +1,6 @@
+'use client'
+import MlfLogo from '@public/mlf-2.svg';
+
+export default function Logo({className}) {
+ return
;
+}
\ No newline at end of file
diff --git a/components/nav/nav-mobile-items.tsx b/components/nav/nav-mobile-items.tsx
new file mode 100644
index 00000000..186449c8
--- /dev/null
+++ b/components/nav/nav-mobile-items.tsx
@@ -0,0 +1,25 @@
+'use client'
+import { useSearchParams } from 'next/navigation'
+
+export default function NavMobileItems({ navs }: { navs: any }) {
+ const searchParams = useSearchParams()
+ const draft = searchParams.get('draft')
+ const filteredNavs = draft ? navs : navs.filter((item)=>{return item.visible})
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/components/nav/nav-mobile.tsx b/components/nav/nav-mobile.tsx
index f0bf6369..13d1319d 100644
--- a/components/nav/nav-mobile.tsx
+++ b/components/nav/nav-mobile.tsx
@@ -1,25 +1,49 @@
'use client'
-import { useSearchParams } from 'next/navigation'
-export default function NavMobile({ navs }: { navs: any }) {
- const searchParams = useSearchParams()
- const draft = searchParams.get('draft')
- const filteredNavs = draft ? navs : navs.filter((item)=>{return item.visible})
+import React, { Suspense, useState } from "react";
+import Link from "next/link";
+import NavMobileItems from "./nav-mobile-items";
+import Logo from '@public/mlf-2.svg';
+import { Dialog, DialogPanel } from '@headlessui/react'
+import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'
+
+export default function NavMobile({ title, navs }: { title: string, navs: any}) {
+ const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
+
return (
-
-
+
+
+
+
+
+
+
+ >
)
-}
\ No newline at end of file
+}
+
diff --git a/docs/mlf-colour-full.svg b/docs/mlf-colour-full.svg
new file mode 100644
index 00000000..1a2baf96
--- /dev/null
+++ b/docs/mlf-colour-full.svg
@@ -0,0 +1,411 @@
+
+
+
diff --git a/functions/send_email/lambda_function.py b/functions/send_email/lambda_function.py
index ef141f33..bb98b734 100644
--- a/functions/send_email/lambda_function.py
+++ b/functions/send_email/lambda_function.py
@@ -49,12 +49,13 @@ def generate_standard_ticket_body(data):
with open("./send_email/ticket_body.html", "r") as body_file:
body_tmpl = Template(body_file.read())
+ subdomain = "www" if os.environ.get("STAGE_NAME") == "prod" else os.environ.get("STAGE_NAME")
body = body_tmpl.substitute({
'fullname':data['name'],
'email':data['email'],
'ticketnumber':data['ticket_number'],
'rows':rows,
- 'ticket_link':"http://app.merseysidelatinfestival.co.uk/preferences?email={}&ticket_number={}".format(data['email'], data['ticket_number']),
+ 'ticket_link':"http://{}.merseysidelatinfestival.co.uk/preferences?email={}&ticket_number={}".format(subdomain,data['email'], data['ticket_number']),
'total_row':total_row,
'heading_message':data['heading_message'],
})
diff --git a/functions/serverless.yml b/functions/serverless.yml
index 7d2e5096..18a52c23 100644
--- a/functions/serverless.yml
+++ b/functions/serverless.yml
@@ -29,12 +29,10 @@ stages:
deletionPolicy: Delete
skipTables: true
githubBranchDestination: "develop"
- stripeSecretKey: ${param:testStripeSecretKey}
prod:
params:
isProd: true
deletionPolicy: Retain
- stripeSecretKey: ${param:prodStripeSecretKey}
githubBranchDestination: "main"
diff --git a/functions/serverless.yml.live b/functions/serverless.yml.live
deleted file mode 100644
index 426042ce..00000000
--- a/functions/serverless.yml.live
+++ /dev/null
@@ -1,297 +0,0 @@
-org: danceenginesystems # Needs a name for the organization
-app: dance-engine # Need to see what connor thinks of name
-service: api # Do we need multiple services?
-frameworkVersion: '4.1'
-
-plugins:
- - serverless-prune-plugin
-
-custom:
- prune:
- automatic: true
- includeLayers: true
- number: 10
-
-package:
- individually: true
-
-stages:
- default:
- observability: true
- params:
- attendeesTableName: "${sls:stage}-mlf-attendees"
- stripeProductsTableName: "${sls:stage}-mlf-stripe-products"
- isProd: false
- prod:
- params:
- isProd: true
-
-
-provider:
- name: aws
- runtime: nodejs18.x
- memorySize: 128
- region: eu-west-1
- iam:
- role:
- statements:
- - Effect: Allow
- Action:
- - dynamodb:Query
- - dynamodb:Scan
- - dynamodb:GetItem
- - dynamodb:PutItem
- - dynamodb:UpdateItem
- - dynamodb:DeleteItem
- Resource:
- - Fn::GetAtt: [AttendeesTable, Arn]
- - Fn::GetAtt: [StripeProductsTable, Arn]
- - { "Fn::Join": [ "/", [
- { "Fn::GetAtt": ["AttendeesTable", "Arn" ] }, "index", "ticket_number-index"
- ]]}
- - Effect: Allow
- Action:
- - lambda:InvokeFunction
- Resource:
- - '*'
-
-layers:
- dynamodb:
- path: _layers/dynamodb/python
- stripe:
- path: _layers/stripe/python
- sendmail:
- path: _layers/sendmail/python
- github:
- path: _layers/github/python
-
-resources:
- Resources:
- AttendeesTable:
- Type: AWS::DynamoDB::Table
- DeletionPolicy: Retain
- Properties:
- DeletionProtectionEnabled: ${param:isProd}
- PointInTimeRecoverySpecification:
- PointInTimeRecoveryEnabled: ${param:isProd}
- AttributeDefinitions:
- - AttributeName: email
- AttributeType: S
- - AttributeName: ticket_number
- AttributeType: S
- KeySchema:
- - AttributeName: email
- KeyType: HASH
- - AttributeName: ticket_number
- KeyType: RANGE
- BillingMode: PAY_PER_REQUEST
- TableName: ${param:attendeesTableName}
- GlobalSecondaryIndexes:
- - IndexName: ticket_number-index
- KeySchema:
- - AttributeName: ticket_number
- KeyType: HASH
- Projection:
- ProjectionType: 'ALL'
- StripeProductsTable:
- Type: AWS::DynamoDB::Table
- DeletionPolicy: Retain
- Properties:
- DeletionProtectionEnabled: ${param:isProd}
- AttributeDefinitions:
- - AttributeName: prod_id
- AttributeType: S
- - AttributeName: price_id
- AttributeType: S
- KeySchema:
- - AttributeName: prod_id
- KeyType: HASH
- - AttributeName: price_id
- KeyType: RANGE
- BillingMode: PAY_PER_REQUEST
- TableName: ${param:stripeProductsTableName}
-
-functions:
- Example:
- handler: example/lambda_function.example
-
-# CardPayment:
-# runtime: python3.11
-# handler: card_payment/lambda_function.lambda_handler
-# name: "${sls:stage}-card_payment"
-# package:
-# patterns:
-# - '!**/**'
-# - "card_payment/**"
-# environment:
-# PRODUCTS_TABLE_NAME: ${param:stripeProductsTableName}
-# layers:
-# - !Ref DynamodbLambdaLayer
-# events:
-# - httpApi:
-# path: /card_payment
-# method: post
-
- # CheckoutComplete:
- # handler: checkout_complete/lambda_function.lambda_handler
- # name: "${sls:stage}-checkout_complete"
- # package:
- # patterns:
- # - '!**/**'
- # - "checkout_complete/**"
- # environment:
- # ATTENDEES_TABLE_NAME: ${param:attendeesTableName}
- # STRIPE_SECRET_KEY: ${param:stripeSecretKey}
- # layers:
- # - !Ref DynamodbLambdaLayer
- # - !Ref SendmailLambdaLayer
- # events:
- # - httpApi:
- # path: /checkout_complete
- # method: post
-
- # CreateTicket:
- # handler: create_ticket/lambda_function.lambda_handler
- # name: "${sls:stage}-create_ticket"
- # package:
- # patterns:
- # - '!**/**'
- # - "create_ticket/**"
- # environment:
- # ATTENDEES_TABLE_NAME: ${param:attendeesTableName}
- # STRIPE_SECRET_KEY: ${param:stripeSecretKey}
- # layers:
- # - !Ref DynamodbLambdaLayer
-
- # CustomerPreferences:
- # handler: customer_preferences/lambda_function.lambda_handler
- # name: "${sls:stage}-customer-preferences"
- # package:
- # patterns:
- # - '!**/**'
- # - "customer_preferences/**"
- # environment:
- # ATTENDEES_TABLE_NAME: ${param:attendeesTableName}
- # layers:
- # - !Ref DynamodbLambdaLayer
- # - !Ref GithubLambdaLayer
- # events:
- # - httpApi:
- # path: /customer_preferences
- # method: get
- # - httpApi:
- # path: /customer_preferences
- # method: post
-
- # GeneratePricingUpdate:
- # handler: gen_price_update/lambda_function.lambda_handler
- # name: "${sls:stage}-gen_price_update"
- # package:
- # patterns:
- # - '!**/**'
- # - "gen_price_update/**"
- # environment:
- # PRODUCTS_TABLE_NAME: ${param:stripeProductsTableName}
- # STRIPE_SECRET_KEY: ${param:stripeSecretKey}
- # GITHUB_TOKEN: ${param:githubToken}
- # layers:
- # - !Ref StripeLambdaLayer
-
- # GetAttendees:
- # handler: get_attendees/lambda_function.lambda_handler
- # name: "${sls:stage}-get_attendees"
- # package:
- # patterns:
- # - '!**/**'
- # - "get_attendees/**"
- # environment:
- # ATTENDEES_TABLE_NAME: ${param:attendeesTableName}
- # layers:
- # - !Ref DynamodbLambdaLayer
- # - !Ref StripeLambdaLayer
- # events:
- # - httpApi:
- # path: /get_attendees
- # method: get
-
- # ScanTicket:
- # handler: scan_ticket/lambda_function.lambda_handler
- # name: "${sls:stage}-scan_ticket"
- # package:
- # patterns:
- # - '!**/**'
- # - "scan_ticket/**"
- # environment:
- # ATTENDEES_TABLE_NAME: ${param:attendeesTableName}
- # layers:
- # - !Ref DynamodbLambdaLayer
- # - !Ref GithubLambdaLayer
- # events:
- # - httpApi:
- # path: /scan_ticket
- # method: get
- # - httpApi:
- # path: /scan_ticket
- # method: post
-
- # SendEmail:
- # handler: send_email/lambda_function.lambda_handler
- # name: "${sls:stage}-send_email"
- # package:
- # patterns:
- # - '!**/**'
- # - "send_email/**"
- # environment:
- # SENDGRID_API_KEY: ${param:sendgridApiKey}
-
- # SendTicket:
- # handler: send_ticket/lambda_function.lambda_handler
- # name: "${sls:stage}-send_ticket"
- # package:
- # patterns:
- # - '!**/**'
- # - "send_ticket/**"
- # environment:
- # ATTENDEES_TABLE_NAME: ${param:attendeesTableName}
- # layers:
- # - !Ref DynamodbLambdaLayer
- # - !Ref SendmailLambdaLayer
- # - !Ref GithubLambdaLayer
- # events:
- # - httpApi:
- # path: /send_ticket
- # method: post
-
- # StripePriceUpdate:
- # handler: stripe_price_update/lambda_function.lambda_handler
- # name: "${sls:stage}-stripe_price_update"
- # package:
- # patterns:
- # - '!**/**'
- # - "stripe_price_update/**"
- # environment:
- # ATTENDEES_TABLE_NAME: ${param:attendeesTableName}
- # PRODUCTS_TABLE_NAME: ${param:stripeProductsTableName}
- # STRIPE_SECRET_KEY: ${param:stripeSecretKey}
- # layers:
- # - !Ref DynamodbLambdaLayer
- # events:
- # - httpApi:
- # path: /stripe_price_update
- # method: post
-
- # TransferOwner:
- # handler: transfer_owner/lambda_function.lambda_handler
- # name: "${sls:stage}-transfer_owner"
- # package:
- # patterns:
- # - '!**/**'
- # - "transfer_owner/**"
- # environment:
- # ATTENDEES_TABLE_NAME: ${param:attendeesTableName}
- # layers:
- # - !Ref DynamodbLambdaLayer
- # events:
- # - httpApi:
- # path: /transfer_owner
- # method: post
\ No newline at end of file
diff --git a/lib/authorise.ts b/lib/authorise.ts
new file mode 100644
index 00000000..2f8599a9
--- /dev/null
+++ b/lib/authorise.ts
@@ -0,0 +1,37 @@
+const superuser = "superadmin"
+const grantUsage = {
+ "all-admins": ["/admin"],
+ "developer": ["#","/admin/users","/admin/stripe","/admin/import"],
+ "content-manager": ['/admin/content'],
+ "door-staff": ['/admin/ticketing','/admin/scan', '/admin/epos'],
+}
+// const grantView = {
+// "developer": {
+// "thing": ["create","update","read","delete"],
+// "otherthing": ["create","update","read","delete"]
+// }
+// }
+
+
+export const authUsage = (user,path) => {
+ // Check could ever be allowed to do anything
+ if(!authBasic(user)) return false
+
+ const pathWithoutQueryString = path.split('?')[0]
+ const roles = user.publicMetadata.roles
+ // Superuser gets set to true always
+ if(roles.includes(superuser)) return true
+ // check through roles to see if any of them allow access to path
+ return roles.some((role) => {
+ if(!grantUsage[role]) return false // Roles doesn't exist in permissions
+ return grantUsage[role] && grantUsage[role].includes(pathWithoutQueryString)
+ })
+}
+
+const authBasic = (user) => {
+ // Make sure has metadata, admin and some roles
+ if(!user || !user.publicMetadata || !user.publicMetadata.admin || !user.publicMetadata.roles ) return false
+ // Does it have roles?
+ if(!Array.isArray(user.publicMetadata.roles)) return false
+ return true
+}
\ No newline at end of file
diff --git a/lib/useful.ts b/lib/useful.ts
index b82de024..1c9d5d71 100644
--- a/lib/useful.ts
+++ b/lib/useful.ts
@@ -1,3 +1,15 @@
export const deepCopy = (object: any) => {
return JSON.parse(JSON.stringify(object))
+}
+
+export const guaranteeISOstringFromDate = (date: string | number) => {
+ return isNaN(Date.parse(date as string))
+ ? new Date(parseInt(date as string) * 1000).toISOString()
+ : new Date(Date.parse(date as string))
+}
+
+export const guaranteeTimestampFromDate = (date: string | number) => {
+ return isNaN(Date.parse(date as string))
+ ? parseInt(date as string) * 1000
+ : Date.parse(date as string)
}
\ No newline at end of file
diff --git a/serverless.yml b/serverless.yml
deleted file mode 100644
index dd6e6e11..00000000
--- a/serverless.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-org: danceenginesystems # Needs a name for the organization
-app: dance-engine # Need to see what connor thinks of name
-service: api # Do we need multiple services?
-frameworkVersion: '4.1'
-
-provider:
- name: aws
- runtime: python3.11
-
-functions:
- hello:
- handler: scan_ticket/lambda_function.lambda_handler