Merge branch 'feature/contact-page' into 'master'
Feature/contact page v0.1 See merge request sahkoinsinoorikilta/vtmk/web2.0-frontend!3
This commit is contained in:
+14
-3
@@ -5,7 +5,9 @@
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended"
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react/recommended"
|
||||
],
|
||||
"globals": {
|
||||
"Atomics": "readonly",
|
||||
@@ -47,7 +49,7 @@
|
||||
"no-extra-boolean-cast": "warn",
|
||||
"no-param-reassign": "error",
|
||||
"no-shadow": "warn",
|
||||
"no-unused-vars": "warn",
|
||||
"no-unused-vars": "off",
|
||||
"no-useless-constructor": "warn",
|
||||
"object-curly-newline": "error",
|
||||
"prefer-destructuring": "warn",
|
||||
@@ -70,6 +72,7 @@
|
||||
"jsx-a11y/no-static-element-interactions": "off",
|
||||
"jsx-a11y/label-has-associated-control": "off",
|
||||
"jsx-a11y/label-has-for": "off",
|
||||
"react/display-name": "off",
|
||||
"react/destructuring-assignment": "off",
|
||||
"react/jsx-closing-bracket-location": "off",
|
||||
"react/jsx-closing-tag-location": "off",
|
||||
@@ -82,6 +85,14 @@
|
||||
"react/no-access-state-in-setstate": "warn",
|
||||
"react/prop-types": "off",
|
||||
"react/prefer-stateless-function": "off",
|
||||
"react/self-closing-comp": "off"
|
||||
"react/self-closing-comp": "off",
|
||||
"@typescript-eslint/camelcase": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": "off",
|
||||
"@typescript-eslint/indent": "off",
|
||||
"@typescript-eslint/interface-name-prefix": "off",
|
||||
"@typescript-eslint/no-empty-interface": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unused-vars": "warn"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
@@ -0,0 +1,14 @@
|
||||
@import "../../assets/scss/globals";
|
||||
|
||||
.committee-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
color: color('dark-blue');
|
||||
|
||||
&__contacts {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import * as React from "react";
|
||||
import "./CommitteeContainer.scss";
|
||||
import { Occupation } from "../../models/Contacts";
|
||||
import ContactCard from "../ContactCard/ContactCard";
|
||||
|
||||
export interface CommitteeContainerProps {
|
||||
name_fi: string;
|
||||
name_en: string;
|
||||
contacts: Occupation[];
|
||||
}
|
||||
export interface CommitteeContainerState { }
|
||||
|
||||
class CommitteeContainer extends React.Component<CommitteeContainerProps, CommitteeContainerState> {
|
||||
render() {
|
||||
const { name_fi, name_en, contacts } = this.props;
|
||||
return (
|
||||
<div className="committee-container">
|
||||
<div>
|
||||
{name_fi}
|
||||
</div>
|
||||
<div>
|
||||
{name_en}
|
||||
</div>
|
||||
<div className="committee-container__contacts">
|
||||
{contacts.map(occupation => (
|
||||
occupation.officials.map(official => (
|
||||
<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}
|
||||
/>
|
||||
))
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CommitteeContainer;
|
||||
@@ -0,0 +1,2 @@
|
||||
import CommitteeContainer from "./CommitteeContainer";
|
||||
export default CommitteeContainer;
|
||||
@@ -2,92 +2,34 @@
|
||||
|
||||
|
||||
.contact-card {
|
||||
background-color: color(white);
|
||||
color: color(dark-blue);
|
||||
white-space: wrap;
|
||||
margin: 1rem 1rem;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: flex-start;
|
||||
width: calc(25% - 2rem);
|
||||
flex-flow: row nowrap;
|
||||
color: color(dark-blue);
|
||||
margin: 1rem 1rem;
|
||||
|
||||
@media screen and (min-width: 1000px) and (max-width: 1200px - 1px) {
|
||||
width: calc(50% - 2rem);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1000px - 1px) {
|
||||
width: 100%;
|
||||
margin-bottom: 3rem;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&__title {
|
||||
padding: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
&__info {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
padding: 0.5rem;
|
||||
justify-content: flex-start;
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
color: color(black);
|
||||
color: color('dark-blue');
|
||||
}
|
||||
|
||||
&__image {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
margin-bottom: 1rem;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
|
||||
@media screen and (min-width: 1200px) {
|
||||
height: 15vw;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px - 1px) {
|
||||
height: 35vh;
|
||||
}
|
||||
}
|
||||
|
||||
&__button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
button {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 16px;
|
||||
margin: 0 0 0.5rem;
|
||||
font-weight: 200;
|
||||
line-height: 22px;
|
||||
color: color(black);
|
||||
|
||||
@media screen and (max-width: 1200px - 1px) {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__datetime {
|
||||
color: color(orange1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
|
||||
@media screen and (max-width: 1200px - 1px) {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 16px;
|
||||
&__container {
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
max-height: 100px;
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,34 @@
|
||||
import * as React from "react";
|
||||
import "./ContactCard.scss";
|
||||
// @ts-ignore
|
||||
import * as blank_profile from "../../assets/img/blank_profile.png";
|
||||
import { Role } from "../../models/Contacts";
|
||||
|
||||
export interface ContactCardProps {
|
||||
first_name: String;
|
||||
last_name: String;
|
||||
phone: Number;
|
||||
email: String;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
phone: number;
|
||||
email: string;
|
||||
image: string;
|
||||
role: Role;
|
||||
}
|
||||
export interface ContactCardState {}
|
||||
export interface ContactCardState { }
|
||||
|
||||
class ContactCard extends React.Component<ContactCardProps, ContactCardState> {
|
||||
render() {
|
||||
const { first_name, last_name, phone, email } = this.props;
|
||||
const { first_name, last_name, phone, email, image, role } = this.props;
|
||||
return (
|
||||
<div className="contact-card">
|
||||
<div className="contact-card__firstname">{first_name}</div>
|
||||
<div className="contact-card__lastname">{last_name}</div>
|
||||
<div className="contact-card__phone">{phone}</div>
|
||||
<div className="contact-card__email">{email}</div>
|
||||
<div className="contact-card__image__container">
|
||||
<img className="contact-card__image" src={image ? image : blank_profile} alt="profile_image" />
|
||||
</div>
|
||||
<div className="contact-card__info">
|
||||
<div className="contact-card__name">{`${first_name} ${last_name}`}</div>
|
||||
<div className="contact-card__phone">{phone}</div>
|
||||
<div className="contact-card__email">{email}</div>
|
||||
<div className="contact-card__role">{role.name_fi}</div>
|
||||
<div className="contact-card__role">{role.name_en}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface NavbarChildLinkProps {
|
||||
to: string;
|
||||
}
|
||||
export interface NavbarChildLinkState {
|
||||
open: Boolean;
|
||||
open: boolean;
|
||||
}
|
||||
|
||||
class NavbarChildLink extends React.Component<NavbarChildLinkProps, NavbarChildLinkState> {
|
||||
|
||||
@@ -9,8 +9,8 @@ export interface NavbarDropdownLinkProps {
|
||||
exploded?: boolean; // if exploded, show items directly underneath without a dropdown menu
|
||||
}
|
||||
export interface NavbarDropdownLinkState {
|
||||
mouseOverLink: Boolean;
|
||||
mouseOverBox: Boolean;
|
||||
mouseOverLink: boolean;
|
||||
mouseOverBox: boolean;
|
||||
}
|
||||
|
||||
class NavbarDropdownLink extends React.Component<NavbarDropdownLinkProps, NavbarDropdownLinkState> {
|
||||
|
||||
+35
-4
@@ -1,19 +1,50 @@
|
||||
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 {
|
||||
id: number;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
phone_number: number;
|
||||
email: string;
|
||||
role: string;
|
||||
image: string;
|
||||
}
|
||||
|
||||
export async function getContacts(): Promise<Contact[]> {
|
||||
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);
|
||||
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);
|
||||
|
||||
@@ -16,7 +16,7 @@ export interface SignupForm {
|
||||
export async function getForms(): Promise<SignupForm[]> {
|
||||
try {
|
||||
const resp = await axios.get(url);
|
||||
const results = resp.data["results"];
|
||||
const { results } = resp.data;
|
||||
return results;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import * as React from "react";
|
||||
import Helmet from "react-helmet";
|
||||
import "./ContactsPage.scss";
|
||||
import { ColorEnum } from "../../components/ColorDiv/ColorDiv";
|
||||
import { ColorEnum, getColor, getHoverColor } from "../../components/ColorDiv/ColorDiv";
|
||||
import { StaticContext } from "../../server/StaticContext";
|
||||
import PageLink from "../../components/PageLink/PageLink";
|
||||
import Card from "../../components/Card";
|
||||
import PageSection from "../../components/PageSection";
|
||||
import HeroMainSection from "../../components/HeroMainSection";
|
||||
import ContactCard from "../../components/ContactCard";
|
||||
import { Contact, getContacts } from "../../models/Contacts";
|
||||
import { getContacts, Occupation, Committee, getCommittees } from "../../models/Contacts";
|
||||
import CommitteeContainer from "../../components/CommitteeContainer";
|
||||
import Anchor from "../../components/Anchor/index";
|
||||
|
||||
interface ContactsPageProps {
|
||||
staticContext: StaticContext;
|
||||
}
|
||||
|
||||
interface ContactsPageState {
|
||||
contacts: Contact[];
|
||||
contacts: Occupation[];
|
||||
committees: Committee[];
|
||||
}
|
||||
|
||||
class ContactsPage extends React.Component<ContactsPageProps, ContactsPageState> {
|
||||
@@ -29,22 +28,29 @@ class ContactsPage extends React.Component<ContactsPageProps, ContactsPageState>
|
||||
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 Contact[];
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,31 +64,47 @@ class ContactsPage extends React.Component<ContactsPageProps, ContactsPageState>
|
||||
return getContactsPromise;
|
||||
}
|
||||
|
||||
fetchCommittees = () => {
|
||||
const getCommitteesPromise = getCommittees();
|
||||
getCommitteesPromise.then(committees => {
|
||||
this.setState({
|
||||
committees,
|
||||
});
|
||||
});
|
||||
return getCommitteesPromise;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { contacts } = this.state;
|
||||
const { contacts, committees } = this.state;
|
||||
const board = contacts.filter(x => x.role.is_board);
|
||||
return (
|
||||
<div className="contacts-page">
|
||||
<Helmet>
|
||||
<link rel="canonical" href="https://sik.ayy.fi/INSERT_PATH_HERE!" />
|
||||
</Helmet>
|
||||
Contacts Page
|
||||
<HeroMainSection>
|
||||
<h1>Aalto-yliopiston Sähköinsinöörikilta</h1>
|
||||
<PageSection backgroundColor={ColorEnum.White} textColor={ColorEnum.DarkBlue}>
|
||||
<p>
|
||||
lorem ipsum dolor est
|
||||
Asiaa olisi, mutta kehen ottaa yhteyttä?<br />
|
||||
Tämä sivu yrittää valottaa sen oikean ihmisen puhelinnumeroa ja sähköpostiosoitetta.
|
||||
</p>
|
||||
</HeroMainSection>
|
||||
<PageSection backgroundColor={ColorEnum.White}>
|
||||
{contacts.map(contact => (
|
||||
<ContactCard
|
||||
key={contact.id}
|
||||
first_name={contact.first_name}
|
||||
last_name={contact.last_name}
|
||||
phone={contact.phone_number}
|
||||
email={contact.email}
|
||||
/>
|
||||
))}
|
||||
</PageSection>
|
||||
<PageSection backgroundColor={ColorEnum.White} textColor={ColorEnum.DarkBlue} bottomBorder>
|
||||
<div>
|
||||
<CommitteeContainer name_fi="Hallitus" name_en="Board" contacts={board} />
|
||||
<p>
|
||||
Hallitukseen saa yhteyden lähettämällä sähköpostia <Anchor
|
||||
className={`${getColor(ColorEnum.DarkBlue)} ${getHoverColor(ColorEnum.LightBlue)}`}
|
||||
to="mailto:sik-hallitus@list.ayy.fi">
|
||||
sik-hallitus@list.ayy.fi
|
||||
</Anchor>
|
||||
</p>
|
||||
</div>
|
||||
</PageSection>
|
||||
{committees.map((committee, index) => (
|
||||
<PageSection key={index} backgroundColor={ColorEnum.White}>
|
||||
<CommitteeContainer name_fi={committee.name_fi} name_en={committee.name_en} contacts={contacts.filter(x => x.role.committee.name_fi === committee.name_fi)} />
|
||||
</PageSection>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class EventCreatePage extends React.Component<EventCreatePageProps, EventCreateP
|
||||
|
||||
this.fetchTags();
|
||||
|
||||
const id = props.match.params.id;
|
||||
const { id } = props.match.params;
|
||||
if (id !== undefined) {
|
||||
this.fetchInitialFormData(id);
|
||||
}
|
||||
@@ -217,17 +217,17 @@ class EventCreatePage extends React.Component<EventCreatePageProps, EventCreateP
|
||||
<link rel="canonical" href="https://sik.ayy.fi/admin/events/create" />
|
||||
</Helmet>
|
||||
<h1>{title}</h1>
|
||||
{ statusMessage && <div className="success">{ statusMessage }</div>}
|
||||
{statusMessage && <div className="success">{statusMessage}</div>}
|
||||
<Form schema={schema}
|
||||
uiSchema={uiSchema}
|
||||
formData={formData}
|
||||
idPrefix="rjsf"
|
||||
widgets={widgets}
|
||||
onChange={this.onChange}
|
||||
onSubmit={this.onSubmit}
|
||||
onError={this.onError}
|
||||
onFocus={this.onFocus} />
|
||||
{ error && <div className="error">{error}</div> }
|
||||
uiSchema={uiSchema}
|
||||
formData={formData}
|
||||
idPrefix="rjsf"
|
||||
widgets={widgets}
|
||||
onChange={this.onChange}
|
||||
onSubmit={this.onSubmit}
|
||||
onError={this.onError}
|
||||
onFocus={this.onFocus} />
|
||||
{error && <div className="error">{error}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export interface SignUpPageProps {
|
||||
match: {
|
||||
params: {
|
||||
id: number;
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,9 +69,9 @@ class SignUpPage extends React.Component<SignUpPageProps, SignUpPageState> {
|
||||
return <input type="text" name={question.id} />;
|
||||
}
|
||||
if (question.type === "radiobutton") {
|
||||
const options = question.options;
|
||||
return options.map((opt) => (
|
||||
<span>
|
||||
const { options } = question;
|
||||
return options.map((opt, index) => (
|
||||
<span key={index}>
|
||||
<input type="radio" name={`${question.id}`} />
|
||||
{opt}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user