Feature contacts

This commit is contained in:
ojakoo
2021-01-08 22:44:07 +02:00
parent 2659e9b438
commit c1eab0452b
8 changed files with 296 additions and 225 deletions
+159
View File
@@ -0,0 +1,159 @@
{
"name_fi": "Hallitus",
"name_en": "Board",
"roles": [
{
"name_fi": "Puheenjohtaja",
"name_en": "Chairman",
"representatives": [
{
"name": "Johannes Ora"
}
]
},
{
"name_fi": "Sihteeri",
"name_en": "Secretary",
"representatives": [
{
"name": "Salla Lyytikäinen",
"phone_number": null,
"email": null,
"image": null
}
]
},
{
"name_fi": "Rahastonhoitaja",
"name_en": "",
"representatives": [
{
"name": "Santeri Huhtala",
"phone_number": null,
"email": null,
"image": null
}
]
},
{
"name_fi": "Fuksitoimikunnan Puheenjohtaja",
"name_en": "",
"representatives": [
{
"name": "Toni Ojala",
"phone_number": null,
"email": null,
"image": null
}
]
},
{
"name_fi": "Fuksitoimikunnan puheenjohtajan adjutantti",
"name_en": "",
"representatives": [
{
"name": "Toni Lyttinen",
"phone_number": null,
"email": null,
"image": null
}
]
},
{
"name_fi": "Hovimestari",
"name_en": "",
"representatives": [
{
"name": "Eveliina Ahonen",
"phone_number": null,
"email": null,
"image": null
}
]
},
{
"name_fi": "Hovineuvos",
"name_en": "",
"representatives": [
{
"name": "Melissa Dönmez",
"phone_number": null,
"email": null,
"image": null
}
]
},
{
"name_fi": "ISOvastaava",
"name_en": "",
"representatives": [
{
"name": "Heidi Mäkitalo",
"phone_number": null,
"email": null,
"image": null
}
]
},
{
"name_fi": "Hyvinvointimestari",
"name_en": "",
"representatives": [
{
"name": "Sauli Norja",
"phone_number": null,
"email": null,
"image": null
}
]
},
{
"name_fi": "Opintomestari",
"name_en": "",
"representatives": [
{
"name": "Simo Hakanummi",
"phone_number": null,
"email": null,
"image": null
}
]
},
{
"name_fi": "Teknologiamestari",
"name_en": "",
"representatives": [
{
"name": "Oskari Ponkala",
"phone_number": null,
"email": null,
"image": null
}
]
},
{
"name_fi": "Ulkomestari",
"name_en": "",
"representatives": [
{
"name": "Oliver Hiekkamies",
"phone_number": null,
"email": null,
"image": null
}
]
},
{
"name_fi": "Yrityssuhdemestari",
"name_en": "",
"representatives": [
{
"name": "Otto Julkunen",
"phone_number": null,
"email": null,
"image": null
}
]
}
]
}
+30
View File
@@ -0,0 +1,30 @@
{
"name_fi": "Hyvinvointitoimikunta",
"name_en": "Wellbeing comittee",
"roles": [
{
"name_fi": "Puheenjohtaja",
"name_en": "Chairman",
"representatives": [
{
"name": "Oskari Ponkala",
"phone_number": null,
"email": null,
"image": null
}
]
},
{
"name_fi": "Web-kisälli",
"name_en": "Web-journeyman",
"representatives": [
{
"name": "Ilari Ojakorpi",
"phone_number": null,
"email": null,
"image": null
}
]
}
]
}
+16 -20
View File
@@ -1,18 +1,17 @@
import React from "react";
import styled from "styled-components";
import { Occupation } from "@models/Contacts";
import ContactCard from "./ContactCard";
import { Committee } from "@views/ContactsPage/ContactsPageView"
import { colors } from "@theme/colors";
interface CommitteeContainerProps {
name_fi: string;
name_en: string;
contacts: Occupation[];
committee: Committee;
}
const Container = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
color: ${colors.darkBlue};
@@ -28,29 +27,26 @@ const Container = styled.div`
& > div {
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
justify-content: center;
}
`;
const CommitteeContainer: React.FC<CommitteeContainerProps> = ({ name_fi, name_en, contacts }) => (
const CommitteeContainer: React.FC<CommitteeContainerProps> = ({ committee }) => (
<Container>
<p>
{name_fi}
</p>
<p>
{name_en}
{committee.name_fi||committee.name_en}
</p>
<div>
{contacts.map(occupation => (
occupation.officials.map(official => (
{committee.roles.map(role => (
role.representatives.map(representative => (
<ContactCard
key={official.first_name}
first_name={official.first_name}
last_name={official.last_name}
phone={official.phone_number}
email={official.email}
image={official.image}
role={occupation.role}
key={representative.name}
name={representative.name}
phone={representative.phone_number}
email={representative.email}
image={representative.image}
role_fi={role.name_fi}
role_en={role.name_en}
/>
))
))}
@@ -58,4 +54,4 @@ const CommitteeContainer: React.FC<CommitteeContainerProps> = ({ name_fi, name_e
</Container>
);
export default CommitteeContainer;
export default CommitteeContainer;
+27 -23
View File
@@ -1,24 +1,26 @@
import React from "react";
import styled from "styled-components";
import blank_profile from "@assets/img/blank_profile.png";
import { Role } from "@models/Contacts";
import { colors } from "@theme/colors";
const Card = styled.article`
display: flex;
align-items: flex-start;
flex-flow: row nowrap;
padding: 0.5rem;
color: ${colors.darkBlue};
margin: 1rem;
width: 15rem;
min-width: 300px;
`;
const ImageContainer = styled.div`
padding: 1rem;
padding: 0.5rem;
display: flex;
flex-shrink: 0;
justify-content: center;
align-items: center;
max-height: 100px;
max-width: 100px;
height: 6rem;
width: 6rem;
& > img {
width: 100%;
@@ -30,41 +32,43 @@ const ImageContainer = styled.div`
const Info = styled.div`
display: flex;
flex-direction: column;
padding: 0.5rem;
justify-content: flex-start;
font-size: 1rem;
font-weight: 300;
align-items: flex-start;
padding: 0.25rem;
font-size: 0.9rem;
color: ${colors.darkBlue};
`;
const Name = styled.text`
font-size: 1.2rem;
font-weight: 600;
`
interface ContactCardProps {
first_name: string;
last_name: string;
phone: number;
name: string;
phone: string;
email: string;
image: string;
role: Role;
role_fi: string;
role_en: string;
}
const ContactCard: React.FC<ContactCardProps> = ({ first_name, last_name, phone, email, image, role }) => {
const fullName = `${first_name} ${last_name}`;
const ContactCard: React.FC<ContactCardProps> = ({ name, phone, email, image, role_fi, role_en }) => {
return(
<Card>
<ImageContainer>
<img
src={image || blank_profile}
alt={fullName}
alt={name}
/>
</ImageContainer>
<Info>
<p>{fullName}</p>
<p>{phone}</p>
<p>{email}</p>
<p>{role.name_fi}</p>
<p>{role.name_en}</p>
<Name>{name}</Name>
<text>{role_fi || role_en}</text>
{phone ? <text>{phone}</text> : null}
{email ? <text>{email}</text> : null}
</Info>
</Card>
</Card>
)
}
export default ContactCard;
export default ContactCard;
-53
View File
@@ -1,53 +0,0 @@
import axios from "axios";
const url = `${process.env.API_URL}/contacts`;
const committeeUrl = `${process.env.API_URL}/committees`;
export interface Committee {
name_fi: string;
name_en: string;
}
export interface Role {
name_fi: string;
name_en: string;
description_fi: string;
description_en: string;
committee: Committee;
is_board: boolean;
}
export interface Contact {
first_name: string;
last_name: string;
phone_number: number;
email: string;
image: string;
}
export interface Occupation {
role: Role;
start_date: string;
end_date: string;
officials: Contact[];
}
export async function getContacts(year: number = new Date().getFullYear()): Promise<Occupation[]> {
try {
const resp = await axios.get(`${url}?year=${year}`);
return resp.data["results"];
} catch (err) {
console.error(err);
throw err;
}
}
export async function getCommittees(): Promise<Committee[]> {
try {
const resp = await axios.get(`${committeeUrl}`);
return resp.data["results"];
} catch (err) {
console.error(err);
throw err;
}
}
+9 -81
View File
@@ -1,86 +1,14 @@
import React from "react";
import { Helmet } from "react-helmet";
import { StaticContext } from "@server/StaticContext";
import { getContacts, Occupation, Committee, getCommittees } from "@models/Contacts";
import ContactsPageView from "@views/ContactsPage/ContactsPageView";
interface ContactsPageProps {
staticContext: StaticContext;
}
const ContactsPage: React.FC = () => (
<>
<Helmet>
<link rel="canonical" href="https://sik.ayy.fi/INSERT_PATH_HERE!" />
</Helmet>
<ContactsPageView />
</>
);
interface ContactsPageState {
contacts: Occupation[];
committees: Committee[];
}
class ContactsPage extends React.Component<ContactsPageProps, ContactsPageState> {
constructor(props: ContactsPageProps) {
super(props);
const { staticContext } = props;
if (staticContext) {
/* The static context is an object that manages promises when
rendering on the server. If staticContext exists, that means
we have to store all promises in it. Otherwise, operate
normally. See server/index.ts. */
if (staticContext.resolutions.getContacts) {
const contacts = staticContext.resolutions.getContacts as Occupation[];
const committees = staticContext.resolutions.getCommittees as Committee[];
this.state = {
contacts,
committees,
};
} else {
this.state = {
contacts: [],
committees: [],
};
const promiseContacts = this.fetchContacts();
const promiseCommittees = this.fetchCommittees();
staticContext.promises.getContacts = promiseContacts;
staticContext.promises.getCommittees = promiseCommittees;
}
} else {
this.state = {
contacts: [],
committees: [],
};
this.fetchContacts();
this.fetchCommittees();
}
}
fetchContacts = () => {
const getContactsPromise = getContacts();
getContactsPromise.then(contacts => {
this.setState({
contacts,
});
});
return getContactsPromise;
}
fetchCommittees = () => {
const getCommitteesPromise = getCommittees();
getCommitteesPromise.then(committees => {
this.setState({
committees,
});
});
return getCommitteesPromise;
}
render() {
const { contacts, committees } = this.state;
return (
<>
<Helmet>
<link rel="canonical" href="https://sik.ayy.fi/INSERT_PATH_HERE!" />
</Helmet>
<ContactsPageView contacts={contacts} committees={committees} />
</>
);
}
}
export default ContactsPage;
export default ContactsPage;
+53 -47
View File
@@ -1,15 +1,11 @@
import React from "react";
import styled from "styled-components";
import { Occupation, Committee } from "@models/Contacts";
import CommitteeContainer from "@components/CommitteeContainer";
import { Divider, TextSection } from "@components/index";
import { colors } from "@theme/colors";
import { Link } from "@components/index";
interface ContactsPageViewProps {
contacts: Occupation[];
committees: Committee[];
}
import BoardJson from "@assets/json/board.json";
import HvtmkJson from "@assets/json/hvtmk.json";
const BlueLink = styled(Link)`
color: ${colors.blue1};
@@ -19,46 +15,56 @@ const BlueLink = styled(Link)`
}
`;
class ContactsPageView extends React.Component<ContactsPageViewProps> {
render() {
const { contacts, committees } = this.props;
const board = contacts.filter(x => x.role.is_board);
return (
<>
<TextSection>
<p>
Asiaa olisi, mutta kehen ottaa yhteyttä?<br />
Tämä sivu yrittää valottaa sen oikean ihmisen puhelinnumeroa ja sähköpostiosoitetta.
</p>
</TextSection>
<TextSection>
<div>
<CommitteeContainer name_fi="Hallitus" name_en="Board" contacts={board} />
<p>
{"Hallitukseen saa yhteyden lähettämällä sähköpostia "}
<BlueLink to="mailto:sik-hallitus@list.ayy.fi">
sik-hallitus@list.ayy.fi
</BlueLink>
</p>
</div>
</TextSection>
<Divider />
{committees.map((committee, index) => {
// const order = committee.name_fi === "Toimikunnattomat" ? 1 : 0;
return (
<>
<TextSection key={index}>
<CommitteeContainer name_fi={committee.name_fi} name_en={committee.name_en} contacts={contacts.filter(x => x.role.committee.name_fi === committee.name_fi)} />
</TextSection>
<Divider />
</>
)
})}
</>
);
}
export interface Committee {
name_fi: string;
name_en: string;
roles: Array<Role>;
}
export default ContactsPageView;
export interface Role {
name_fi: string;
name_en: string;
representatives: Array<Representative>
}
export interface Representative {
name: string;
phone_number?: string;
email?: string;
image?: string;
}
const ContactsPageView: React.FC = () => (
<>
<TextSection>
<p>
Asiaa olisi, mutta kehen ottaa yhteyttä?<br />
Tämä sivu yrittää valottaa sen oikean ihmisen puhelinnumeroa ja sähköpostiosoitetta.
</p>
</TextSection>
<TextSection>
<div>
<CommitteeContainer committee={BoardJson} />
<p>
{"Hallitukseen saa yhteyden lähettämällä sähköpostia "}
<BlueLink to="mailto:sik-hallitus@list.ayy.fi">
sik-hallitus@list.ayy.fi
</BlueLink>
</p>
</div>
</TextSection>
<Divider />
<TextSection >
<div>
<CommitteeContainer committee={HvtmkJson} />
</div>
</TextSection>
<Divider />
</>
)
export default ContactsPageView;
+2 -1
View File
@@ -3,6 +3,7 @@
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": false,
"resolveJsonModule": true,
"esModuleInterop": true,
"experimentalDecorators": true,
// "emitDecoratorMetadata": true,
@@ -60,4 +61,4 @@
"./src/**/*",
"./types/**/*"
],
}
}