Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ model Corps {
language String @default("sv")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
contactURL String?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not 100% convinced adding contactURL to Corps is a good idea. I would prefer adding a new model Position which defines a name, contactUrl and maybe links to some of the permission roles.

E.g. for the Kassör:

{
    name: "Kassör",
    nameEn: "Treasurer",
    email: "kassor@bleckhornen.org",
    contactUrl: null, // Google form for Trivselombud
    permissionRoleId: <id>,
}

Another solution could be to add email and contactUrl to Role, and create new roles for each board member and commitee chairman. Could be worth adding nameEn/nameEnglish as well to store the English translation.


roles Role[]
permissions Permission[]
Expand Down
12 changes: 12 additions & 0 deletions src/app/account/preferences.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const initialValues = {
lactoseFree: false,
otherFoodPrefs: '',
email: '',
contactURL: '',
mainInstrument: '',
};
type FormValues = typeof initialValues;
Expand Down Expand Up @@ -59,6 +60,7 @@ const AccountPreferences = () => {
lactoseFree: corps.foodPrefs?.lactoseFree ?? false,
otherFoodPrefs: corps.foodPrefs?.other ?? '',
email: corps.user.email || undefined,
contactURL: corps.contactURL ?? '',
mainInstrument,
});
}, [corps]);
Expand Down Expand Up @@ -86,6 +88,8 @@ const AccountPreferences = () => {
label: i.instrument.name,
}));

const isTrivselOmbud = corps?.roles.some(role => role.name === "Trivselombud");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to keep the code in English. Although in this case it might be acceptable as we are looking for a role called "Trivselombud".


return (
<form onSubmit={form.onSubmit(handleSubmit)}>
<FormLoadingOverlay visible={submitting || corpsLoading}>
Expand Down Expand Up @@ -155,6 +159,14 @@ const AccountPreferences = () => {
label={lang('Pronomen', 'Pronouns')}
{...form.getInputProps('pronouns')}
/>
{
(isTrivselOmbud) &&
(<TextInput
label={lang('Kontakt URL', 'Contact URL')}
{...form.getInputProps('contactURL')}
/>)
}

</div>
<div className='flex flex-col gap-2 pl-2'>
<h3>{lang('Matpreferenser', 'Food preferences')}</h3>
Expand Down
305 changes: 305 additions & 0 deletions src/app/info/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
import { lang } from 'utils/language';
import PositionInfobox from 'components/corps/position-infobox';
import { api } from 'trpc/server';
import { IconMail } from '@tabler/icons-react';
import ActionIcon from 'components/input/action-icon';


const styrelseOrder: Dictionary<number> = {
"Ordförande": 1,
"ViceOrdförande": 2,
"Sekreterare": 3,
"Kassör": 4,
};
Comment on lines +8 to +13
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems overcomplicated. Just have boardRoles as an array of strings and collect each corps by checking which corps have that role.

You could also define a new API router endpoint getBoardRoles or getCommiteeLeaders which collects all corps in the relevant positions.



const Positions = async () => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be called InfoPage.

const roles = await api.permission.getRoles.query();
const ordoredBoardCorps = await Promise.all(
roles
.filter(role => Object.keys(styrelseOrder).includes(role.name))
.sort((a, b) => styrelseOrder[a.name] - styrelseOrder[b.name])
.flatMap(role =>
role.corpsii.map(async (corps) => {
const result = await api.corps.get.query({ id: corps.id });
if (!result) throw new Error("Corps not found");
return result;
})
)
);

const TrivselCorps = await Promise.all(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be in camelCase.

roles
.filter(role => role.name === "Trivselombud")
.flatMap(role =>
role.corpsii.map(async (corps) => {
const result = await api.corps.get.query({ id: corps.id });
if (!result) throw new Error("Corps not found");
return result;
})
)
Comment on lines +33 to +40
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A good general solution to collecting the different roles should be to extend the getRole endpoint to be able to take a name as input.

);

return (
<div>
<h2>{lang('Ansvarsposter', 'Positions of responsibility')}</h2>
<div className='max-w-4xl'>
<div className='flex flex-col space-y-2 p-4'>
<h3>{lang('Styrelsen', 'The Board')}</h3>
{lang(`Styrelsen leder Bleckhornen under ett verksamhetsår. Tillsammans med dirigenter,
balettledare, utskottsmedlemmar och andra förtroendevalda ansvarar styrelsen för planering
av repor, spelningar, sittningar, konserter, resor och andra aktiviteter. Detta görs med stöd och
samarbete med hela corpset`,
Comment on lines +45 to +52
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, we should decide which terms should be capitalized or not, so the use remains consistent. Right now it is a bit mixed.


`The board leads Bleckhornen throughout the year. Together with conductors, ballet leaders,
committee members, and other elected representatives, the board is responsible for planning
rehearsals, performances, formal sittings, concerts, trips, and other activities. This is done
with the support and collaboration of the entire corps.`)}
<div className='grid grid-cols-2 gap-4'>
{ordoredBoardCorps.map((corps) => (
<div className='rounded border shadow-md dark:border-neutral-800 flex flex-col p-2 text-left text-sm' >
{corps.roles.filter(role => Object.keys(styrelseOrder).includes(role.name))[0]?.name}
<PositionInfobox corps={corps} />
</div>
))
}
</div>
</div>
</div>

<div className='max-w-4xl'>
<div className='flex flex-col space-y-2 p-4'>
<h3>Trivselombud</h3>
{lang(`Trivselombuden i Bleckhornen har i uppdrag att bidra till trivsel och trygghet inom
föreningen. Till oss kan du komma om du känner att du har upplevt något inom Bleckhornen
som känns fel, eller om du känner att du behöver stöd eller prata om något. Trivselombuden
kan du alltid komma pch prata med när du känner för det, men du kan också kontakta oss via
våra formulär, där det även finns möjlighet att vara anonym.`,

`The Wellbeing Representatives in Bleckhornen are responsible for promoting comfort and
safety within the association. You can come to us if you've experienced something within
Bleckhornen that feels wrong, or if you feel that you need support or just someone to talk to.
You can always approach the Wellbeing Representatives whenever you feel the need, but you
can also contact us through our forms, where there is also an option to remain anonymous.`)}
{roles.filter((role) => (
role.name == "Trivselombud"
)).map((role) => (
<div key={role.id} className='grid grid-cols-2 gap-4'>
{TrivselCorps.map((corps) => (
<div className='rounded border shadow-md dark:border-neutral-800 flex flex-col p-2 text-left text-sm' >
<PositionInfobox corps={corps} />
</div>
))
}
</div>
))}
</div>
</div>

<div className='max-w-4xl'>
<div className='flex flex-col space-y-2 p-4'>
<h3>Utskott</h3>
{lang(`I Bleckhornen finns flera utskott, där varje utskott har sina specifika uppgifter.
Tillsammans ser de till att föreningen fungerar smidigt och utvecklas.
Det finns ett utskott för alla, där din kreativitet och personlighet får flöda.
Samtidigt får du även chansen att lära känna personer från olika sektioner.`,

`In Bleckhornen, there are several committees, each with its own specific tasks.
Together, they ensure that the association runs smoothly and continues to develop.
There’s a committee for everyone, where your creativity and personality can shine.
At the same time, you also get the chance to meet and get to know people from different sections.`)}

<div className='flex gap-1'>
<h4>Notmarskeriet</h4>
<ActionIcon
href={`mailto:notmarsk.bleckhornen@gmail.com`}
variant='subtle'
>
<IconMail />
</ActionIcon>
</div>
{lang(`Notmarskeriet ansvarar för alla Bleckhornens noter. Detta innebär att vi skriver ut 📠, sätter
in 📒, och arkiverar ️ alla de noter som finns i våra pärmar och häften! Arbetsbördan som
notmarsk är generellt koncentrerad runt julkonserten och karnevalerna, och då kan den vara
rätt stor, men under den tidiga hösten och samt så gott som hela våren de år Lund ej gästas av
karneval är oftast relativt lite att göra.`,

`The "Notmarskeri" (roughly "Note Marshallery") is responsible for Bleckhornen's sheet
music. This means that we print 📠, insert 📒, and archive ️ all the sheet music that can be
found in our folders and booklets! A note marshal's workload is typically concentrated
around the Christmas concert and the Karnevals, and at those points it can be a bit, but early
Autumn and basically all of the spring semester (on non-karneval years) are typically very
free`)}

<div className='flex gap-1'>
<h4>Arkivet</h4>
<ActionIcon
href={`mailto:arkivet.bleckhornen@gmail.com`}
variant='subtle'
>
<IconMail />
</ActionIcon>
</div>
{lang(`Arkivet är utskottet som förevigar allt skoj! Vi ser till att spara minnen från det roliga vi gör,
bland annat i form av bilder, filmer och affischer som corps har producerat. Vi sparar också
resultat av det slit andra funktionärer lägger ner, som corpset skulle kunna behöva i
framtiden.`,

`The Archive is the committee that immortalizes all the fun! We make sure to preserve
memories from all the enjoyable things we do. For example, in the form of photos, videos,
and posters produced by the corps. We also keep the results of the hard work put in by other
functionaries, which the corps might need in the future.`)}

<div className='flex gap-1'>
<h4>PR</h4>
<ActionIcon
href={`mailto:bleckhornen.pr@gmail.com`}
variant='subtle'
>
<IconMail />
</ActionIcon>
</div>
{lang(`PR är utskottet som ser till att vi syns och hörs även utöver spelningarna! Vårt arbete är
främst att sköta våra sociala medier och fota på spelningar. Inför julkonserten gör vi även
affischerna, programbladen och söker spons. I detta utskott får kreativa idéer komma till liv!`,

`PR is the committee that makes sure we’re seen and heard even beyond our performances!
Our work mainly involves managing our social media and taking photos at gigs. Before the
Christmas concert, we also create the posters and programs and handle sponsorships. In this
committee, creative ideas come to life!`)}

<div className='flex gap-1'>
<h4>Baren</h4>
<ActionIcon
href={`mailto:baren.bleckhornen@gmail.com`}
variant='subtle'
>
<IconMail />
</ActionIcon>
</div>
{lang(`Det är baren som förser corpset med grogg! (Ber du snällt kanske du till och med kan få en drink 😉)
Den första torsdagen varje månad bjuder vi in till extra festlig eftersits med extra festlig dryck!
Vi serverar dessutom fördrink inför corpsafton, och rattar Bussbaren hela vägen upp till SOF och STORK.
Eins, zwei, drei, gesoffa!`,

`The Bar is the committee that keeps the corps supplied with drinks! (If you ask nicely, you might even get a cocktail 😉)
On the first Thursday of every month, we host an extra festive afterparty with extra festive beverages!
We also serve pre-drinks before corps evenings and run the Bus Bar all the way to SOF and STORK.`)}

<div className='flex gap-1'>
<h4>Sexmästeriet</h4>

</div>
{lang(`Sexmästeriet är utskottet som ser till att ingen går hungrig! Vi lagar mat inför både Vårcorps och Höstcorps,
och ser till att hela corpset får njuta av god mat.
Tillsammans handlar vi ingredienser, lagar maten och har det riktigt roligt!
Som medlem i Sexmästeriet får du också vara med och bestämma vad som ska lagas (och presentera maten under middagarna).`,

`The Culinary Committee makes sure no one goes hungry!
We cook for both Vårcorps and Höstcorps, making sure the whole corps gets to enjoy tasty food.
Together, we shop for ingredients, cook, and have a lot of fun along the way!
As a member, you also get to help decide what’s on the menu (and show off your creations at the dinners).`)}

<div className='flex gap-1'>
<h4>ITK</h4>
<ActionIcon
href={`mailto:itk@bleckhornen.org`}
variant='subtle'
>
<IconMail />
</ActionIcon>
</div>
{lang(`ITK har ansvar för drift av alla Bleckhornens hemsidor, samt vidareutveckling av Blindtarmen.
Driftansvaret includerar blindtarmen, den publika hemsidan och vår interna wiki.`,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Blindtarmen" should be capitalized.


`ITK has responsibility for the operation of all Bleckhornens websites, as well as developing Blindtarmen.
The operational responsebility includes Blindtarmen, the public website, and our internal wiki`)}

<div className='flex gap-1'>
<h4>Pryl & prov</h4>
<ActionIcon
href={`mailto:prylochprovbleckhornen@gmail.com `}
variant='subtle'
>
<IconMail />
</ActionIcon>
</div>
{lang(`I pryl & prov har vi ansvar för corpsets merch och provelever!
Vi försöker se till att proveleverna känner sig välkomna i föreningen
och att de alltid har någon att rikta frågor till om föreningen.
Detta gör vi genom att anordna tillställningar som t.ex. provelevsfördrinkar och en provelevsdag!
När det kommer till merchen köper vi in och säljer föreningens merch,
och ibland när vi får feeling designar vi också ny merch!!`,

`In Pryl & Prov, we’re responsible for the corps’ merch and for the newmembers!
We make sure that new members feel welcome in Bleckhornen
and that they always have someone to turn to with questions about how things work.
We do this by organizing events such as pre-drinks for the new members and a special probationary members’ day!
When it comes to merch, we handle the purchasing and sales of the orchestra's merchandise and sometimes,
when we’re feeling inspired, we even design new merch ourselves!`)}

<div className='flex gap-1'>
<h4>Materialförvaltarna</h4>
<ActionIcon
href={`mailto:materialforvaltare.bleckhornen@gmail.com `}
variant='subtle'
>
<IconMail />
</ActionIcon>
</div>
{lang(`Materialförvaltarna tar hand om och utvecklar Tarmen och ser till Bleckhornens prylar fungerar.
Har du ett roligt projekt du skulle vilja genomföra kan du alltid dryfta din idé med oss för att få tips och stöd.`,

`The materials managers take care of and develop Tarmen and make sure the Bleckhorns’ equipment works.
If you have a fun project you’d like to carry out, you can always discuss your idea with us to get tips and support.`)}

<div className='flex gap-1'>
<h4>Medaljeriet</h4>
<ActionIcon
href={`mailto:medaljeriet.bleckhornen@gmail.com`}
variant='subtle'
>
<IconMail />
</ActionIcon>
</div>
{lang(`Vi i Medaljeriet håller koll på vilka medaljer som ska köpas in och delas ut
och ger på så sätt corpsaftnarna och julkoncertsbanketten det där lilla extra!
Vi designar också de temaenliga julkoncertsmedaljerna varje år!
Utskottets finurliga tolkning av temat blir en fin souvenir till alla deltagande corps.`,

`We in the Medal committee keep track of which medals are to be ordered and given out,
and thus we bring that extra shine to the dinner parties and the Christmas concert banquet!
We also design the Christmas concert medals in accordance with the concert's theme each year!
The committee's clever interpretation of the theme ends up as a nice souvenir for all participating corps.`)}

<div className='flex gap-1'>

<h4>Import</h4>

</div>
{lang(`Vi i importen ser till att det finns den finaste ölen och cidern till ett överkomligt pris.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leave out the "överkomligt pris" part. We are not a store.

Därför åker vi på roadtrips över Öresund och med färjan över Fehmarnbältet för att köpa de bästa danska produkterna i Tyskland.`,

`We from the import committee make sure with the finest beer and cider for an affordable prize.
Therefore we go on roadtrips across the Öresund and with the ferry over Fehmarn belt to buy the best Danish products in Germany.`)}

<div className='flex gap-1'>
<h4>Export</h4>

</div>
{lang(`Vi i Exporten ser till att corpset aldrig går hungriga!
Vi fyller på med snacks, dryck och såklart billys!
Oavsett om det är rep, spelning, så ser vi till att corpset håller humöret på topp.`,

`Exporten makes sure the corps never goes hungry!
We keep the snacks and drinks flowing and of course, plenty of Billy’s!
Whether it’s a rehearsal or a gig, we make sure the corps stays happy, energized, and ready to play.`)}

</div>
</div>
</div>
);
};

export default Positions;

11 changes: 11 additions & 0 deletions src/app/links/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ const Links = () => {
{lang('Notdriven', 'The Note Drive')}
</a>
</li>
<li>
<a
className='cursor-pointer text-red-600 underline'
href='https://samply.app/p/Ub5REp3lbAi3CCnJdgdm'
>
{lang(
'Samply',
"Samply",
Comment on lines +58 to +59
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a more descriptive title.

)}
</a>
</li>
<li>
<a
className='cursor-pointer text-red-600 underline'
Expand Down
3 changes: 3 additions & 0 deletions src/components/corps-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const initialValues = {
number: '',
bNumber: '',
email: '',
contactURL: '',
mainInstrument: '',
otherInstruments: [] as string[],
roles: [] as Permission[],
Expand Down Expand Up @@ -78,6 +79,7 @@ const CorpsForm = ({ corpsId }: AdminCorpsProps) => {
number: corps.number?.toString() || '',
bNumber: corps.bNumber?.toString() || '',
email: corps.user.email ?? '',
contactURL: corps.contactURL ?? '',
mainInstrument,
otherInstruments,
roles: corps.roles.map((r) => r.name as Permission),
Expand Down Expand Up @@ -133,6 +135,7 @@ const CorpsForm = ({ corpsId }: AdminCorpsProps) => {
</div>
<TextInput label='Smeknamn' {...form.getInputProps('nickName')} />
<TextInput label='Pronomen' {...form.getInputProps('pronouns')} />
<TextInput label='Kontakt URL' {...form.getInputProps('contactURL')} />
<div className='flex gap-4'>
<TextInput label='Nummer' {...form.getInputProps('number')} />
<TextInput label='Balettnr.' {...form.getInputProps('bNumber')} />
Expand Down
Loading