formatting, readme, webp images, updated officers
- added prettier config - added missing officers - reordered officers based on official roster - removed unused assets - added project description - fixed mismatching licenses - optimized images to webp - reduced project size by 95%
2
.gitignore
vendored
|
@ -1,3 +1,3 @@
|
|||
build
|
||||
dist
|
||||
node_modules
|
||||
node_modules
|
||||
|
|
4
.prettierrc
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"tabWidth": 4,
|
||||
"useTabs": false
|
||||
}
|
29
README.md
|
@ -1,2 +1,27 @@
|
|||
# NewWebsite
|
||||
The new IEEE website for the section at UCSD
|
||||
# IEEE @ UCSD Website
|
||||
|
||||
## Deployment
|
||||
|
||||
Github Actions have been set to trigger on each push and pull request on the `main` branch. The website will be built as static pages to the `page` branch where it will be deployed onto [ieeeucsd.edu](https://ieeeucsd.edu).
|
||||
|
||||
## Contributing
|
||||
|
||||
Please create a new branch for development (i.e. `[NAME]-dev`). Pushing directly to main is not advised, as changes will go straight into production.
|
||||
|
||||
### Testing
|
||||
|
||||
To build the site, run `npm run build`.
|
||||
|
||||
To view the site on your local network, run `npm build/index.js`. View the site at [localhost:9000](http://localhost:9000).
|
||||
|
||||
### Images
|
||||
|
||||
Large images should be in WebP format. Consider resizing images based on their usage.
|
||||
|
||||
[Squoosh](https://squoosh.app/) is a great online tool for optimizing images developed by Google Chrome Labs.
|
||||
|
||||
### Formatting
|
||||
|
||||
Install [prettier](https://prettier.io/) and use it as the default Typescript and Javascript formatter. The `.prettierrc` configuration file controls the formatting rules.
|
||||
|
||||
For CSS, use `CSS Language Features` as the default formatter. For HTML, use `HTML Language Features` as the default formatter.
|
||||
|
|
2547
package-lock.json
generated
17
package.json
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"name": "ieee-website",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"description": "Website for IEEE of UC San Diego",
|
||||
"main": "build/index.js",
|
||||
"scripts": {
|
||||
"build": "webpack & tsc & time /T",
|
||||
"build": "webpack & tsc",
|
||||
"watch": "npm-watch build"
|
||||
},
|
||||
"watch": {
|
||||
|
@ -19,7 +19,7 @@
|
|||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/node": "^16.11.7",
|
||||
|
@ -35,15 +35,22 @@
|
|||
"@babel/preset-env": "^7.15.8",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@types/react": "^16.14.20",
|
||||
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
||||
"babel-loader": "^8.2.3",
|
||||
"copy-webpack-plugin": "^6.4.1",
|
||||
"css-loader": "^5.2.7",
|
||||
"eslint": "^8.26.0",
|
||||
"eslint-config-standard-with-typescript": "^23.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-n": "^15.3.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-react": "^7.31.10",
|
||||
"html-webpack-plugin": "^4.5.2",
|
||||
"license-webpack-plugin": "^2.3.21",
|
||||
"style-loader": "^2.0.0",
|
||||
"ts-loader": "^8.3.0",
|
||||
"typescript": "^4.4.4",
|
||||
"typescript": "^4.8.4",
|
||||
"webpack": "^5.60.0",
|
||||
"webpack-cli": "^4.9.1"
|
||||
}
|
||||
}
|
||||
}
|
400
src/Database.ts
|
@ -1,208 +1,236 @@
|
|||
import {Schema, Document, LeanDocument} from "mongoose";
|
||||
import { Schema, Document, LeanDocument } from "mongoose";
|
||||
import * as mongoose from "mongoose";
|
||||
import {promisify} from "util";
|
||||
import {randomBytes, pbkdf2} from "crypto";
|
||||
import { promisify } from "util";
|
||||
import { randomBytes, pbkdf2 } from "crypto";
|
||||
|
||||
const pbkdf = promisify(pbkdf2);
|
||||
|
||||
const User = mongoose.model("User", new Schema({
|
||||
display: String,
|
||||
email: {
|
||||
public: Boolean,
|
||||
text: String,
|
||||
},
|
||||
picture: String,
|
||||
password: {
|
||||
hash: Buffer,
|
||||
salt: Buffer
|
||||
},
|
||||
bio: {type: String, default: ""},
|
||||
isOfficer: {type: Boolean, default: false},
|
||||
title: {type: String, default: "Member"},
|
||||
uuid: String
|
||||
}));
|
||||
const User = mongoose.model(
|
||||
"User",
|
||||
new Schema({
|
||||
display: String,
|
||||
email: {
|
||||
public: Boolean,
|
||||
text: String,
|
||||
},
|
||||
picture: String,
|
||||
password: {
|
||||
hash: Buffer,
|
||||
salt: Buffer,
|
||||
},
|
||||
bio: { type: String, default: "" },
|
||||
isOfficer: { type: Boolean, default: false },
|
||||
title: { type: String, default: "Member" },
|
||||
uuid: String,
|
||||
})
|
||||
);
|
||||
|
||||
export interface DBUser extends Document {
|
||||
display: string;
|
||||
email: string;
|
||||
picture: string;
|
||||
password: HashSalt
|
||||
isOfficer: boolean;
|
||||
title: string;
|
||||
uuid: string;
|
||||
display: string;
|
||||
email: string;
|
||||
picture: string;
|
||||
password: HashSalt;
|
||||
isOfficer: boolean;
|
||||
title: string;
|
||||
uuid: string;
|
||||
}
|
||||
interface HashSalt {
|
||||
hash: Buffer;
|
||||
salt: Buffer;
|
||||
hash: Buffer;
|
||||
salt: Buffer;
|
||||
}
|
||||
|
||||
export default class UserDatabase {
|
||||
// How many iterations pbkdf2 will iterate the password hashing algorithm
|
||||
private static PW_ITERATIONS = 100000;
|
||||
// The length of the salt that should be used with every password hash
|
||||
private static SALT_LENGTH = 64;
|
||||
// How many bytes will be used to generate a UUID of length 2 times this variable
|
||||
private static UUID_LENGTH = 32;
|
||||
|
||||
// How many iterations pbkdf2 will iterate the password hashing algorithm
|
||||
private static PW_ITERATIONS = 100000;
|
||||
// The length of the salt that should be used with every password hash
|
||||
private static SALT_LENGTH = 64;
|
||||
// How many bytes will be used to generate a UUID of length 2 times this variable
|
||||
private static UUID_LENGTH = 32;
|
||||
/**
|
||||
* Makes a new UserDatabase
|
||||
* @param url the mongodb database url to connect to
|
||||
*/
|
||||
constructor(url: string) {
|
||||
mongoose.connect(url);
|
||||
mongoose.set("returnOriginal", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a new UserDatabase
|
||||
* @param url the mongodb database url to connect to
|
||||
*/
|
||||
constructor(url: string) {
|
||||
mongoose.connect(url);
|
||||
mongoose.set("returnOriginal", false);
|
||||
}
|
||||
// Functions to edit current users
|
||||
/**
|
||||
* Changes a user's display name on the website
|
||||
* @param uuid the UUID of the user
|
||||
* @param newName the user's new name
|
||||
*/
|
||||
public async setName(uuid: string, newName: string) {
|
||||
await User.findOneAndUpdate({ uuid: uuid }, { name: newName }).exec();
|
||||
}
|
||||
/**
|
||||
* Gets the display name of a user with a given UUID
|
||||
* @param uuid the UUID of the user
|
||||
* @returns the user's display name
|
||||
*/
|
||||
public async getName(uuid: string): Promise<string> {
|
||||
return ((await User.findOne({ uuid: uuid }).lean().exec()) as DBUser)
|
||||
.display;
|
||||
}
|
||||
|
||||
// Functions to edit current users
|
||||
/**
|
||||
* Changes a user's display name on the website
|
||||
* @param uuid the UUID of the user
|
||||
* @param newName the user's new name
|
||||
*/
|
||||
public async setName(uuid: string, newName: string) {
|
||||
await User.findOneAndUpdate({uuid: uuid}, {name: newName}).exec();
|
||||
}
|
||||
/**
|
||||
* Gets the display name of a user with a given UUID
|
||||
* @param uuid the UUID of the user
|
||||
* @returns the user's display name
|
||||
*/
|
||||
public async getName(uuid: string): Promise<string> {
|
||||
return (await User.findOne({uuid: uuid}).lean().exec() as DBUser).display;
|
||||
}
|
||||
/**
|
||||
* Updates a user's display email
|
||||
* @param uuid the UUID of the user
|
||||
* @param newEmail the user's new display email
|
||||
*/
|
||||
public async setEmail(uuid: string, newEmail: string) {
|
||||
await User.findOneAndUpdate({ uuid: uuid }, { email: newEmail }).exec();
|
||||
}
|
||||
/**
|
||||
* Gets the email of a user with a given UUID
|
||||
* @param uuid the UUID of the user
|
||||
* @returns the user's email
|
||||
*/
|
||||
public async getEmail(uuid: string): Promise<string> {
|
||||
return ((await User.findOne({ uuid: uuid }).lean().exec()) as DBUser)
|
||||
.email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a user's display email
|
||||
* @param uuid the UUID of the user
|
||||
* @param newEmail the user's new display email
|
||||
*/
|
||||
public async setEmail(uuid: string, newEmail: string) {
|
||||
await User.findOneAndUpdate({uuid: uuid}, {email: newEmail}).exec();
|
||||
}
|
||||
/**
|
||||
* Gets the email of a user with a given UUID
|
||||
* @param uuid the UUID of the user
|
||||
* @returns the user's email
|
||||
*/
|
||||
public async getEmail(uuid: string): Promise<string> {
|
||||
return (await User.findOne({uuid: uuid}).lean().exec() as DBUser).email;
|
||||
}
|
||||
/**
|
||||
* Updates a user's password
|
||||
* @param uuid the UUID of the user
|
||||
* @param newPassword the user's new password
|
||||
*/
|
||||
public async setPassword(uuid: string, newPassword: string) {
|
||||
let hashsalt = await UserDatabase.hash(newPassword);
|
||||
await User.findOneAndUpdate(
|
||||
{ uuid: uuid },
|
||||
{ password: hashsalt }
|
||||
).exec();
|
||||
}
|
||||
/**
|
||||
* Gets the password of a user with a given UUID
|
||||
* @param uuid the UUID of the user
|
||||
* @returns the user's password
|
||||
*/
|
||||
public async getPasswordHash(uuid: string): Promise<Buffer> {
|
||||
return ((await User.findOne({ uuid: uuid }).lean().exec()) as DBUser)
|
||||
.password.hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a user's password
|
||||
* @param uuid the UUID of the user
|
||||
* @param newPassword the user's new password
|
||||
*/
|
||||
public async setPassword(uuid: string, newPassword: string) {
|
||||
let hashsalt = await UserDatabase.hash(newPassword);
|
||||
await User.findOneAndUpdate({uuid: uuid}, {password: hashsalt}).exec();
|
||||
}
|
||||
/**
|
||||
* Gets the password of a user with a given UUID
|
||||
* @param uuid the UUID of the user
|
||||
* @returns the user's password
|
||||
*/
|
||||
public async getPasswordHash(uuid: string): Promise<Buffer> {
|
||||
return (await User.findOne({uuid: uuid}).lean().exec() as DBUser).password.hash;
|
||||
}
|
||||
/**
|
||||
* Updates a member's position
|
||||
* @param uuid the UUID of the user
|
||||
* @param newTitle the user's new officer title/position
|
||||
*/
|
||||
public async setRank(uuid: string, newTitle: string, isOfficer: boolean) {
|
||||
await User.findOneAndUpdate(
|
||||
{ uuid: uuid },
|
||||
{ title: newTitle, isOfficer: isOfficer }
|
||||
).exec();
|
||||
}
|
||||
/**
|
||||
* Gets the title of a user with a given UUID
|
||||
* @param uuid the UUID of the user
|
||||
* @returns the user's title
|
||||
*/
|
||||
public async getRank(uuid: string): Promise<string> {
|
||||
return ((await User.findOne({ uuid: uuid }).lean().exec()) as DBUser)
|
||||
.title;
|
||||
}
|
||||
/**
|
||||
* Gets whether of a user with a given UUID is an officer
|
||||
* @param uuid the UUID of the user
|
||||
* @returns true if the user is an officer
|
||||
*/
|
||||
public async isOfficer(uuid: string): Promise<boolean> {
|
||||
return ((await User.findOne({ uuid: uuid }).lean().exec()) as DBUser)
|
||||
.isOfficer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a member's position
|
||||
* @param uuid the UUID of the user
|
||||
* @param newTitle the user's new officer title/position
|
||||
*/
|
||||
public async setRank(uuid: string, newTitle: string, isOfficer: boolean) {
|
||||
await User.findOneAndUpdate({uuid: uuid}, {title: newTitle, isOfficer: isOfficer}).exec();
|
||||
}
|
||||
/**
|
||||
* Gets the title of a user with a given UUID
|
||||
* @param uuid the UUID of the user
|
||||
* @returns the user's title
|
||||
*/
|
||||
public async getRank(uuid: string): Promise<string> {
|
||||
return (await User.findOne({uuid: uuid}).lean().exec() as DBUser).title;
|
||||
}
|
||||
/**
|
||||
* Gets whether of a user with a given UUID is an officer
|
||||
* @param uuid the UUID of the user
|
||||
* @returns true if the user is an officer
|
||||
*/
|
||||
public async isOfficer(uuid: string): Promise<boolean> {
|
||||
return (await User.findOne({uuid: uuid}).lean().exec() as DBUser).isOfficer;
|
||||
}
|
||||
/**
|
||||
* Sets the user's new profile picture
|
||||
* @param uuid the UUID of the user
|
||||
* @param newPic the link to the new URL of the profile picture
|
||||
*/
|
||||
public async setPicture(uuid: string, newPic: string) {
|
||||
await User.findOneAndUpdate({ uuid: uuid }, { picture: newPic }).exec();
|
||||
}
|
||||
/**
|
||||
* Gets the profile picture of a user with a given UUID
|
||||
* @param uuid the UUID of the user
|
||||
* @returns the user's profile picture
|
||||
*/
|
||||
public async getPicture(uuid: string): Promise<string> {
|
||||
return ((await User.findOne({ uuid: uuid }).lean().exec()) as DBUser)
|
||||
.picture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user's new profile picture
|
||||
* @param uuid the UUID of the user
|
||||
* @param newPic the link to the new URL of the profile picture
|
||||
*/
|
||||
public async setPicture(uuid: string, newPic: string) {
|
||||
await User.findOneAndUpdate({uuid: uuid}, {picture: newPic}).exec();
|
||||
}
|
||||
/**
|
||||
* Gets the profile picture of a user with a given UUID
|
||||
* @param uuid the UUID of the user
|
||||
* @returns the user's profile picture
|
||||
*/
|
||||
public async getPicture(uuid: string): Promise<string> {
|
||||
return (await User.findOne({uuid: uuid}).lean().exec() as DBUser).picture;
|
||||
}
|
||||
public async getUser(uuid: string): Promise<DBUser> {
|
||||
return User.findOne({ uuid: uuid }).exec() as Promise<DBUser>;
|
||||
}
|
||||
|
||||
public async getUser(uuid: string): Promise<DBUser> {
|
||||
return User.findOne({uuid: uuid}).exec() as Promise<DBUser>;
|
||||
}
|
||||
// User creation and deletion functions
|
||||
/**
|
||||
* Deletes a user with the given UUID
|
||||
* @param uuid the UUID of the user
|
||||
*/
|
||||
public async deleteUser(uuid: string) {
|
||||
await User.findOneAndDelete({ uuid: uuid }).exec();
|
||||
}
|
||||
|
||||
// User creation and deletion functions
|
||||
/**
|
||||
* Deletes a user with the given UUID
|
||||
* @param uuid the UUID of the user
|
||||
*/
|
||||
public async deleteUser(uuid: string) {
|
||||
await User.findOneAndDelete({uuid: uuid}).exec();
|
||||
}
|
||||
/**
|
||||
* Makes a new user and adds it to the database
|
||||
* @param display the user's display name
|
||||
* @param email the user's display email
|
||||
* @param picture the user's picture
|
||||
* @param password the user's plaintext password
|
||||
*/
|
||||
public async makeNewUser(
|
||||
display: string,
|
||||
email: string,
|
||||
picture: string,
|
||||
password: string,
|
||||
title?: string,
|
||||
isOfficer?: boolean
|
||||
): Promise<void> {
|
||||
let encrypted = await UserDatabase.hash(password);
|
||||
await new User({
|
||||
display: display,
|
||||
email: email,
|
||||
picture: picture,
|
||||
password: encrypted,
|
||||
uuid: UserDatabase.genUUID(),
|
||||
isOfficer: !!isOfficer,
|
||||
title: title ? title : "Member",
|
||||
}).save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a new user and adds it to the database
|
||||
* @param display the user's display name
|
||||
* @param email the user's display email
|
||||
* @param picture the user's picture
|
||||
* @param password the user's plaintext password
|
||||
*/
|
||||
public async makeNewUser(display: string, email: string, picture: string, password: string, title?: string, isOfficer?: boolean): Promise<void> {
|
||||
let encrypted = await UserDatabase.hash(password);
|
||||
await (new User({
|
||||
display: display,
|
||||
email: email,
|
||||
picture: picture,
|
||||
password: encrypted,
|
||||
uuid: UserDatabase.genUUID(),
|
||||
isOfficer: !!isOfficer,
|
||||
title: title ? title : "Member"
|
||||
})).save();
|
||||
}
|
||||
// Utility functions
|
||||
/**
|
||||
* Hashes a password using a salt of 64 random bytes and pbkdf2 with 100000 iterations
|
||||
* @param password the password as a string that should be hashed
|
||||
* @returns the hashed password
|
||||
*/
|
||||
public static async hash(password: string): Promise<HashSalt> {
|
||||
let salt = randomBytes(UserDatabase.SALT_LENGTH);
|
||||
let pass = Buffer.from(String.prototype.normalize(password));
|
||||
return {
|
||||
hash: await pbkdf(
|
||||
pass,
|
||||
salt,
|
||||
UserDatabase.PW_ITERATIONS,
|
||||
512,
|
||||
"sha512"
|
||||
),
|
||||
salt: salt,
|
||||
};
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
/**
|
||||
* Hashes a password using a salt of 64 random bytes and pbkdf2 with 100000 iterations
|
||||
* @param password the password as a string that should be hashed
|
||||
* @returns the hashed password
|
||||
*/
|
||||
public static async hash(password: string): Promise<HashSalt> {
|
||||
let salt = randomBytes(UserDatabase.SALT_LENGTH);
|
||||
let pass = Buffer.from(String.prototype.normalize(password));
|
||||
return {
|
||||
hash: await pbkdf(pass, salt, UserDatabase.PW_ITERATIONS, 512, "sha512"),
|
||||
salt: salt
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a UUID for a user or anything else
|
||||
* @returns a random hex string of length 2 * UUID_LENGTH
|
||||
*/
|
||||
public static genUUID(): string {
|
||||
return Array.from(randomBytes(UserDatabase.UUID_LENGTH))
|
||||
.map(val=>(val.toString(16).padStart(2,"0"))).join("");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Generates a UUID for a user or anything else
|
||||
* @returns a random hex string of length 2 * UUID_LENGTH
|
||||
*/
|
||||
public static genUUID(): string {
|
||||
return Array.from(randomBytes(UserDatabase.UUID_LENGTH))
|
||||
.map((val) => val.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
}
|
||||
}
|
||||
|
|
174
src/index.ts
|
@ -1,121 +1,129 @@
|
|||
import express from "express";
|
||||
import {Request, Response} from "express";
|
||||
import { Request, Response } from "express";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
import UserDatabase from "./Database";
|
||||
|
||||
interface Website {
|
||||
[key: string]: string;
|
||||
sitename: string;
|
||||
title: string;
|
||||
description: string;
|
||||
jsfile: string;
|
||||
cssfile: string;
|
||||
themecolor: string;
|
||||
[key: string]: string;
|
||||
sitename: string;
|
||||
title: string;
|
||||
description: string;
|
||||
jsfile: string;
|
||||
cssfile: string;
|
||||
themecolor: string;
|
||||
}
|
||||
|
||||
const app = express();
|
||||
const template = fs.readFileSync(path.join(__dirname, "public/template.html")).toString();
|
||||
const websites = [{
|
||||
sitename: "index",
|
||||
title: "IEEE at UCSD",
|
||||
description: "",
|
||||
jsfile: "js/index.js",
|
||||
cssfile: "css/styles.css",
|
||||
themecolor: ""
|
||||
},
|
||||
{
|
||||
sitename: "events",
|
||||
title: "IEEE at UCSD",
|
||||
description: "",
|
||||
jsfile: "js/events.js",
|
||||
cssfile: "css/styles.css",
|
||||
themecolor: ""
|
||||
},
|
||||
{
|
||||
sitename: "projects",
|
||||
title: "IEEE at UCSD",
|
||||
description: "",
|
||||
jsfile: "js/projects.js",
|
||||
cssfile: "css/styles.css",
|
||||
themecolor: ""
|
||||
},
|
||||
{
|
||||
sitename: "committees",
|
||||
title: "IEEE at UCSD",
|
||||
description: "",
|
||||
jsfile: "js/committees.js",
|
||||
cssfile: "css/styles.css",
|
||||
themecolor: ""
|
||||
},
|
||||
{
|
||||
sitename: "contact",
|
||||
title: "IEEE at UCSD",
|
||||
description: "",
|
||||
jsfile: "js/index.js",
|
||||
cssfile: "css/styles.css",
|
||||
themecolor: ""
|
||||
}
|
||||
const APP = express();
|
||||
const TEMPLATE = fs
|
||||
.readFileSync(path.join(__dirname, "public/template.html"))
|
||||
.toString();
|
||||
const WEBSITES = [
|
||||
{
|
||||
sitename: "index",
|
||||
title: "IEEE at UCSD",
|
||||
description: "",
|
||||
jsfile: "js/index.js",
|
||||
cssfile: "css/styles.css",
|
||||
themecolor: "",
|
||||
},
|
||||
{
|
||||
sitename: "events",
|
||||
title: "IEEE at UCSD",
|
||||
description: "",
|
||||
jsfile: "js/events.js",
|
||||
cssfile: "css/styles.css",
|
||||
themecolor: "",
|
||||
},
|
||||
{
|
||||
sitename: "projects",
|
||||
title: "IEEE at UCSD",
|
||||
description: "",
|
||||
jsfile: "js/projects.js",
|
||||
cssfile: "css/styles.css",
|
||||
themecolor: "",
|
||||
},
|
||||
{
|
||||
sitename: "committees",
|
||||
title: "IEEE at UCSD",
|
||||
description: "",
|
||||
jsfile: "js/committees.js",
|
||||
cssfile: "css/styles.css",
|
||||
themecolor: "",
|
||||
},
|
||||
{
|
||||
sitename: "contact",
|
||||
title: "IEEE at UCSD",
|
||||
description: "",
|
||||
jsfile: "js/index.js",
|
||||
cssfile: "css/styles.css",
|
||||
themecolor: "",
|
||||
},
|
||||
] as Website[];
|
||||
|
||||
const PORT = 9000;
|
||||
|
||||
// Make the public directory traversible to people online
|
||||
app.use(express.static(path.join(__dirname, "public")));
|
||||
APP.use(express.static(path.join(__dirname, "public")));
|
||||
// Put the cookies as a variable in the request
|
||||
app.use((req: Request, res: Response, next: any)=>{
|
||||
req.cookies = req.headers.cookie;
|
||||
next();
|
||||
APP.use((req: Request, res: Response, next: any) => {
|
||||
req.cookies = req.headers.cookie;
|
||||
next();
|
||||
});
|
||||
// Receive json post requests and urlencoded requests
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({extended: true}));
|
||||
APP.use(express.json());
|
||||
APP.use(express.urlencoded({ extended: true }));
|
||||
|
||||
// Send main page
|
||||
app.get("/", (req: Request, res: Response) => {
|
||||
respond(res, "index");
|
||||
APP.get("/", (req: Request, res: Response) => {
|
||||
respond(res, "index");
|
||||
});
|
||||
app.get("/events", (req: Request, res: Response) => {
|
||||
respond(res, "events");
|
||||
APP.get("/events", (req: Request, res: Response) => {
|
||||
respond(res, "events");
|
||||
});
|
||||
app.get("/projects", (req: Request, res: Response) => {
|
||||
respond(res, "projects");
|
||||
APP.get("/projects", (req: Request, res: Response) => {
|
||||
respond(res, "projects");
|
||||
});
|
||||
app.get("/committees", (req: Request, res: Response) => {
|
||||
respond(res, "committees");
|
||||
APP.get("/committees", (req: Request, res: Response) => {
|
||||
respond(res, "committees");
|
||||
});
|
||||
app.get("/contact", (req: Request, res: Response) => {
|
||||
respond(res, "contact");
|
||||
APP.get("/contact", (req: Request, res: Response) => {
|
||||
respond(res, "contact");
|
||||
});
|
||||
|
||||
/**
|
||||
* Utility functions for above methods
|
||||
*/
|
||||
function respond(res: any, filename: string) {
|
||||
res.set({
|
||||
"Content-Type": "text/html"
|
||||
});
|
||||
res.send(generatePage(filename));
|
||||
res.set({
|
||||
"Content-Type": "text/html",
|
||||
});
|
||||
res.send(generatePage(filename));
|
||||
}
|
||||
|
||||
function generatePage(name: string): string {
|
||||
let site = websites.find(e=>e.sitename===name);
|
||||
let html = template;
|
||||
for (let key of Object.keys(site)) {
|
||||
html = html.replace(new RegExp("\\$" + key.toUpperCase()), site[key]);
|
||||
}
|
||||
return html;
|
||||
let site = WEBSITES.find((e) => e.sitename === name);
|
||||
let html = TEMPLATE;
|
||||
for (let key of Object.keys(site)) {
|
||||
html = html.replace(new RegExp("\\$" + key.toUpperCase()), site[key]);
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
function generateFilePages() {
|
||||
for (let site of websites) {
|
||||
let html = generatePage(site.sitename);
|
||||
fs.writeFileSync(require("path").join(__dirname, "public/", `${site.sitename}.html`), html);
|
||||
}
|
||||
for (let site of WEBSITES) {
|
||||
let html = generatePage(site.sitename);
|
||||
fs.writeFileSync(
|
||||
require("path").join(__dirname, "public/", `${site.sitename}.html`),
|
||||
html
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv[2] === "generate") {
|
||||
generateFilePages();
|
||||
generateFilePages();
|
||||
} else {
|
||||
app.listen(PORT, "127.0.0.1");
|
||||
}
|
||||
APP.listen(PORT, () => {
|
||||
console.log(`Listening on port ${PORT}`);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,171 +1,237 @@
|
|||
// The links that are on the top nav bar that link to a lowercase version of their page
|
||||
export const ACTIVE_PAGES = [{
|
||||
name: "Home",
|
||||
url: "/"
|
||||
}, {
|
||||
name: "Events",
|
||||
url: "/events"
|
||||
}, {
|
||||
name: "Projects",
|
||||
url: "/projects"
|
||||
}, {
|
||||
name: "Committees",
|
||||
url: "/committees"
|
||||
}, {
|
||||
name: "Contact Us",
|
||||
url: "/contact"
|
||||
}];
|
||||
export const ACTIVE_PAGES = [
|
||||
{
|
||||
name: "Home",
|
||||
url: "/",
|
||||
},
|
||||
{
|
||||
name: "Events",
|
||||
url: "/events",
|
||||
},
|
||||
{
|
||||
name: "Projects",
|
||||
url: "/projects",
|
||||
},
|
||||
{
|
||||
name: "Committees",
|
||||
url: "/committees",
|
||||
},
|
||||
{
|
||||
name: "Contact Us",
|
||||
url: "/contact",
|
||||
},
|
||||
];
|
||||
|
||||
// Urls of team photos that will go in the "About" slideshow
|
||||
export const TEAM_PHOTOS: string[] = [];
|
||||
export const PROJECT_SPACE: string[] = [];
|
||||
export const EVENTS: any[] = [];
|
||||
|
||||
export const SOCIALS = [
|
||||
{
|
||||
icon: "img/disc.svg",
|
||||
url: "https://discord.gg/XxfjqZSjca",
|
||||
message: "Join our server",
|
||||
},
|
||||
{
|
||||
icon: "img/fab.svg",
|
||||
url: "https://www.facebook.com/ieeeucsd",
|
||||
message: "ieeeucsd",
|
||||
},
|
||||
{
|
||||
icon: "img/inst.svg",
|
||||
url: "https://www.instagram.com/ieee.ucsd",
|
||||
message: "@ieeeucsd",
|
||||
},
|
||||
];
|
||||
export const EMAIL = [
|
||||
{
|
||||
icon: "img/email.svg",
|
||||
url: "mailto:ieee@eng.ucsd.edu",
|
||||
message: "ieee@eng.ucsd.edu",
|
||||
},
|
||||
];
|
||||
export const EMAIL_WHITE = [
|
||||
{
|
||||
icon: "img/email_white.svg",
|
||||
url: "mailto:ieee@eng.ucsd.edu",
|
||||
message: "ieee@eng.ucsd.edu",
|
||||
},
|
||||
];
|
||||
|
||||
export const SOCIALS = [{
|
||||
icon: "img/disc.svg",
|
||||
url: "https://discord.gg/XxfjqZSjca",
|
||||
message: "Join our server"
|
||||
}, {
|
||||
icon: "img/fab.svg",
|
||||
url: "https://www.facebook.com/ieeeucsd",
|
||||
message: "ieeeucsd"
|
||||
}, {
|
||||
icon: "img/inst.svg",
|
||||
url: "https://www.instagram.com/ieee.ucsd",
|
||||
message: "@ieeeucsd"
|
||||
}];
|
||||
export const EMAIL = [{
|
||||
icon: "img/email.svg",
|
||||
url: "mailto:ieee@eng.ucsd.edu",
|
||||
message: "ieee@eng.ucsd.edu"
|
||||
}];
|
||||
export const EMAIL_WHITE = [{
|
||||
icon: "img/email_white.svg",
|
||||
url: "mailto:ieee@eng.ucsd.edu",
|
||||
message: "ieee@eng.ucsd.edu"
|
||||
}]
|
||||
export const SOCIALS_WHITE = [
|
||||
{
|
||||
icon: "img/disc_white.svg",
|
||||
url: "https://discord.gg/XxfjqZSjca",
|
||||
message: "Join our server",
|
||||
},
|
||||
{
|
||||
icon: "img/fab_white.svg",
|
||||
url: "https://www.facebook.com/ieeeucsd",
|
||||
message: "ieeeucsd",
|
||||
},
|
||||
{
|
||||
icon: "img/inst_white.svg",
|
||||
url: "https://www.instagram.com/ieee.ucsd",
|
||||
message: "@ieeeucsd",
|
||||
},
|
||||
];
|
||||
|
||||
export const SOCIALS_WHITE = [{
|
||||
icon: "img/disc_white.svg",
|
||||
url: "https://discord.gg/XxfjqZSjca",
|
||||
message: "Join our server"
|
||||
}, {
|
||||
icon: "img/fab_white.svg",
|
||||
url: "https://www.facebook.com/ieeeucsd",
|
||||
message: "ieeeucsd"
|
||||
}, {
|
||||
icon: "img/inst_white.svg",
|
||||
url: "https://www.instagram.com/ieee.ucsd",
|
||||
message: "@ieeeucsd"
|
||||
}];
|
||||
export const OFFICERS = [{
|
||||
name: "Darin Tsui",
|
||||
position: "Chair",
|
||||
photo: "img/officers/darin.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Brigette Hacia",
|
||||
position: "Vice Chair Internal",
|
||||
photo: "img/officers/brigette.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Tasnia Jamal",
|
||||
position: "Vice Chair Events",
|
||||
photo: "img/officers/tasnia.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Kevin Chang",
|
||||
position: "Vice Chair Projects",
|
||||
photo: "img/officers/kevin.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Niklas Chang",
|
||||
position: "Events Coordinator",
|
||||
photo: "img/officers/niklas.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Tien Vu",
|
||||
position: "Vice Chair External",
|
||||
photo: "img/officers/tien.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Arjun Sampath",
|
||||
position: "Vice Chair Finance",
|
||||
photo: "img/officers/arjun.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Derek Nguyen",
|
||||
position: "Project Space Chair",
|
||||
photo: "img/officers/derek.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Mohak Vaswani",
|
||||
position: "Technical Chair",
|
||||
photo: "img/officers/mohak.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Parisa Shahabi",
|
||||
position: "Social Chair",
|
||||
photo: "img/officers/parisa.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Vuong Bui",
|
||||
position: "Professional Chair",
|
||||
photo: "img/officers/vuong.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Jason Liang",
|
||||
position: "Professional Chair",
|
||||
photo: "img/officers/jason.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Daniel Chen",
|
||||
position: "Outreach Chair",
|
||||
photo: "img/officers/daniel.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Dennis Liang",
|
||||
position: "Outreach Chair",
|
||||
photo: "img/officers/dennis.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Yash Puneet",
|
||||
position: "Robocup Chair",
|
||||
photo: "img/officers/yash.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Rafaella Gomes",
|
||||
position: "Robocup Chair",
|
||||
photo: "img/officers/rafaella.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Josh Lapidario",
|
||||
position: "Quarterly Projects Chair",
|
||||
photo: "img/officers/josh.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Jiliana Tiu",
|
||||
position: "Webmaster",
|
||||
photo: "img/officers/jiliana.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Stephanie Xu",
|
||||
position: "PR Chair - Graphics",
|
||||
photo: "img/officers/stephanie.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Sankalp Kaushik",
|
||||
position: "PR Chair - Media",
|
||||
photo: "img/officers/sankalp.jpg",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Matthew Mikhailov",
|
||||
position: "Supercomputing Chair",
|
||||
photo: "img/officers/temp.png",
|
||||
email: "email@ucsd.edu"
|
||||
}, {
|
||||
name: "Raymond Wang",
|
||||
position: "Webmaster",
|
||||
photo: "img/officers/temp.png",
|
||||
email: "raymond@ucsd.edu"
|
||||
}];
|
||||
export const OFFICERS = [
|
||||
{
|
||||
name: "Darin Tsui",
|
||||
position: "Chair",
|
||||
photo: "img/officers/darin.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Brigette Hacia",
|
||||
position: "Vice Chair Internal",
|
||||
photo: "img/officers/brigette.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Tasnia Jamal",
|
||||
position: "Vice Chair Events",
|
||||
photo: "img/officers/tasnia.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Kevin Chang",
|
||||
position: "Vice Chair Projects",
|
||||
photo: "img/officers/kevin.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Arjun Sampath",
|
||||
position: "Vice Chair Finance",
|
||||
photo: "img/officers/arjun.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Niklas Chang",
|
||||
position: "Events Coordinator",
|
||||
photo: "img/officers/niklas.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Tien Vu",
|
||||
position: "Vice Chair External",
|
||||
photo: "img/officers/tien.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Derek Nguyen",
|
||||
position: "Project Space Chair",
|
||||
photo: "img/officers/derek.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Rafaella Gomes",
|
||||
position: "Robocup Chair",
|
||||
photo: "img/officers/rafaella.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Yash Puneet",
|
||||
position: "Robocup Chair",
|
||||
photo: "img/officers/yash.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Matthew Mikhailov",
|
||||
position: "Supercomputing Chair",
|
||||
photo: "img/officers/temp.png",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Josh Lapidario",
|
||||
position: "Quarterly Projects Chair",
|
||||
photo: "img/officers/josh.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Sanh Nguyen",
|
||||
position: "Quarterly Projects Chair",
|
||||
photo: "img/officers/temp.png",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Vuong Bui",
|
||||
position: "Professional Chair",
|
||||
photo: "img/officers/vuong.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Jason Liang",
|
||||
position: "Professional Chair",
|
||||
photo: "img/officers/jason.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Mohak Vaswani",
|
||||
position: "Technical Chair",
|
||||
photo: "img/officers/mohak.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Yusuf Morsi",
|
||||
position: "Technical Chair",
|
||||
photo: "img/officers/temp.png",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Shaun Garcia",
|
||||
position: "Technical Chair",
|
||||
photo: "img/officers/temp.png",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Dennis Liang",
|
||||
position: "Outreach Chair",
|
||||
photo: "img/officers/dennis.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Daniel Chen",
|
||||
position: "Outreach Chair",
|
||||
photo: "img/officers/daniel.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Parisa Shahabi",
|
||||
position: "Social Chair",
|
||||
photo: "img/officers/parisa.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Matthew Yik",
|
||||
position: "Social Chair",
|
||||
photo: "img/officers/temp.png",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Jiliana Tiu",
|
||||
position: "Webmaster",
|
||||
photo: "img/officers/jiliana.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Raymond Wang",
|
||||
position: "Webmaster",
|
||||
photo: "img/officers/temp.png",
|
||||
email: "raymond@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Sankalp Kaushik",
|
||||
position: "PR Chair - Media",
|
||||
photo: "img/officers/sankalp.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
{
|
||||
name: "Stephanie Xu",
|
||||
position: "PR Chair - Graphics",
|
||||
photo: "img/officers/stephanie.jpg",
|
||||
email: "email@ucsd.edu",
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as ReactDom from "react-dom";
|
||||
import * as React from "react";
|
||||
import TopBar from "./components/TopBar";
|
||||
import {ACTIVE_PAGES, SOCIALS, EMAIL, OFFICERS} from "./Config";
|
||||
import { ACTIVE_PAGES, SOCIALS, EMAIL, OFFICERS } from "./Config";
|
||||
import Splash from "./components/Splash";
|
||||
import DefaultSection from "./components/DefaultSection";
|
||||
import InvolveBox from "./components/InvolveBox";
|
||||
|
@ -13,46 +13,81 @@ interface MainProps {}
|
|||
interface MainState {}
|
||||
|
||||
class Main extends React.Component<MainProps, MainState> {
|
||||
constructor(props: MainProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
public render() {
|
||||
return <>
|
||||
<TopBar links={ACTIVE_PAGES}></TopBar>
|
||||
<Splash cta="The backbone of IEEE. Come help make a difference!" delay={2000} backgrounds={["img/backgrounds/committee.png"]}></Splash>
|
||||
<DefaultSection title="Join us!" paragraphs={[
|
||||
"Interested in gaining experience of event planning and development, meeting new friends, and learning more about IEEE? Join one of our committees as an IEEEntern!"
|
||||
]}>
|
||||
</DefaultSection>
|
||||
<DefaultSection className={"our-comms"} title="Our Committees">
|
||||
<div className="cards">
|
||||
<InvolveBox boxTitle="" image="img/committees/technical.jpg" description="Technical Committee"></InvolveBox>
|
||||
<InvolveBox boxTitle="" image="img/committees/social.jpg" description="Social Committee"></InvolveBox>
|
||||
<InvolveBox boxTitle="" image="img/committees/professional.jpg" description="Professional Committee"></InvolveBox>
|
||||
<InvolveBox boxTitle="" image="img/committees/pr.jpg" description="PR Committee"></InvolveBox>
|
||||
<InvolveBox boxTitle="" image="img/committees/outreach.jpg" description="Outreach Committee"></InvolveBox>
|
||||
</div>
|
||||
</DefaultSection>
|
||||
|
||||
<div id="contact-us">
|
||||
<DefaultSection title="Interested? Join our socials!">
|
||||
<div className="join-scls">{
|
||||
[...EMAIL, ...SOCIALS].map(n => (
|
||||
<SocialCard url={n.url} image={n.icon} message={n.message}></SocialCard>
|
||||
))
|
||||
}</div>
|
||||
</DefaultSection>
|
||||
<DefaultSection className="contact" title="Or... Contact one of our staff!">
|
||||
<Carousel items={OFFICERS} itemsPerPage={6}></Carousel>
|
||||
</DefaultSection>
|
||||
</div>
|
||||
constructor(props: MainProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
public render() {
|
||||
return (
|
||||
<>
|
||||
<TopBar links={ACTIVE_PAGES}></TopBar>
|
||||
<Splash
|
||||
cta="The backbone of IEEE. Come help make a difference!"
|
||||
delay={2000}
|
||||
backgrounds={["img/backgrounds/committee.webp"]}
|
||||
></Splash>
|
||||
<DefaultSection
|
||||
title="Join us!"
|
||||
paragraphs={[
|
||||
"Interested in gaining experience of event planning and development, meeting new friends, and learning more about IEEE? Join one of our committees as an IEEEntern!",
|
||||
]}
|
||||
></DefaultSection>
|
||||
<DefaultSection className={"our-comms"} title="Our Committees">
|
||||
<div className="cards">
|
||||
<InvolveBox
|
||||
boxTitle=""
|
||||
image="img/committees/technical.webp"
|
||||
description="Technical Committee"
|
||||
></InvolveBox>
|
||||
<InvolveBox
|
||||
boxTitle=""
|
||||
image="img/committees/social.webp"
|
||||
description="Social Committee"
|
||||
></InvolveBox>
|
||||
<InvolveBox
|
||||
boxTitle=""
|
||||
image="img/committees/professional.webp"
|
||||
description="Professional Committee"
|
||||
></InvolveBox>
|
||||
<InvolveBox
|
||||
boxTitle=""
|
||||
image="img/committees/pr.webp"
|
||||
description="PR Committee"
|
||||
></InvolveBox>
|
||||
<InvolveBox
|
||||
boxTitle=""
|
||||
image="img/committees/outreach.webp"
|
||||
description="Outreach Committee"
|
||||
></InvolveBox>
|
||||
</div>
|
||||
</DefaultSection>
|
||||
|
||||
<Footer></Footer>
|
||||
</>;
|
||||
}
|
||||
<div id="contact-us">
|
||||
<DefaultSection title="Interested? Join our socials!">
|
||||
<div className="join-scls">
|
||||
{[...EMAIL, ...SOCIALS].map((n) => (
|
||||
<SocialCard
|
||||
url={n.url}
|
||||
image={n.icon}
|
||||
message={n.message}
|
||||
></SocialCard>
|
||||
))}
|
||||
</div>
|
||||
</DefaultSection>
|
||||
<DefaultSection
|
||||
className="contact"
|
||||
title="Or... Contact one of our staff!"
|
||||
>
|
||||
<Carousel items={OFFICERS} itemsPerPage={6}></Carousel>
|
||||
</DefaultSection>
|
||||
</div>
|
||||
|
||||
<Footer></Footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDom.render(<Main></Main>, document.getElementById("root"));
|
||||
|
||||
export default {};
|
||||
export default {};
|
||||
|
|
|
@ -1,57 +1,81 @@
|
|||
import * as React from "react";
|
||||
import {Component} from "react";
|
||||
import { Component } from "react";
|
||||
import { CarouselItemProps } from "./CarouselItem";
|
||||
import CarouselPage from "./CarouselPage";
|
||||
|
||||
interface CarouselProps {
|
||||
items: CarouselItemProps[];
|
||||
itemsPerPage: number;
|
||||
items: CarouselItemProps[];
|
||||
itemsPerPage: number;
|
||||
}
|
||||
interface CarouselState {
|
||||
page: number;
|
||||
page: number;
|
||||
}
|
||||
|
||||
export default class Carousel extends Component<CarouselProps, CarouselState> {
|
||||
constructor(props: CarouselProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
page: 0
|
||||
};
|
||||
}
|
||||
constructor(props: CarouselProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
page: 0,
|
||||
};
|
||||
}
|
||||
|
||||
private prevPage() {
|
||||
this.setState({page: this.state.page - 1 < 0 ? 0 : this.state.page - 1});
|
||||
}
|
||||
private prevPage() {
|
||||
this.setState({
|
||||
page: this.state.page - 1 < 0 ? 0 : this.state.page - 1,
|
||||
});
|
||||
}
|
||||
|
||||
private nextPage() {
|
||||
this.setState({ page: this.state.page + 1 > Math.ceil(this.props.items.length / this.props.itemsPerPage) - 1 ?
|
||||
Math.ceil(this.props.items.length / this.props.itemsPerPage) - 1 :
|
||||
this.state.page + 1
|
||||
});
|
||||
}
|
||||
private nextPage() {
|
||||
this.setState({
|
||||
page:
|
||||
this.state.page + 1 >
|
||||
Math.ceil(this.props.items.length / this.props.itemsPerPage) - 1
|
||||
? Math.ceil(
|
||||
this.props.items.length / this.props.itemsPerPage
|
||||
) - 1
|
||||
: this.state.page + 1,
|
||||
});
|
||||
}
|
||||
|
||||
public render() {
|
||||
let arr = this.chunkArray(this.props.items);
|
||||
return <div className="carousel">
|
||||
<img className="carousel-left" src="img/arrow.svg" style={
|
||||
this.state.page === 0 ? {visibility: "hidden"} : {}
|
||||
} onClick={this.prevPage.bind(this)}></img>
|
||||
<div className="carousel-items">{
|
||||
arr.map((items, i)=>(
|
||||
<CarouselPage items={items} visible={i===this.state.page}></CarouselPage>
|
||||
))
|
||||
}</div>
|
||||
<img className="carousel-right" src="img/arrow.svg" style={
|
||||
this.state.page === arr.length - 1 ? {visibility: "hidden"} : {}
|
||||
} onClick={this.nextPage.bind(this)}></img>
|
||||
</div>;
|
||||
}
|
||||
public render() {
|
||||
let arr = this.chunkArray(this.props.items);
|
||||
return (
|
||||
<div className="carousel">
|
||||
<img
|
||||
className="carousel-left"
|
||||
src="img/arrow.svg"
|
||||
style={
|
||||
this.state.page === 0 ? { visibility: "hidden" } : {}
|
||||
}
|
||||
onClick={this.prevPage.bind(this)}
|
||||
></img>
|
||||
<div className="carousel-items">
|
||||
{arr.map((items, i) => (
|
||||
<CarouselPage
|
||||
items={items}
|
||||
visible={i === this.state.page}
|
||||
></CarouselPage>
|
||||
))}
|
||||
</div>
|
||||
<img
|
||||
className="carousel-right"
|
||||
src="img/arrow.svg"
|
||||
style={
|
||||
this.state.page === arr.length - 1
|
||||
? { visibility: "hidden" }
|
||||
: {}
|
||||
}
|
||||
onClick={this.nextPage.bind(this)}
|
||||
></img>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private chunkArray(array: CarouselItemProps[]):CarouselItemProps[][] {
|
||||
let returnArr = [] as CarouselItemProps[][];
|
||||
for(let i = 0; i < array.length; i+=this.props.itemsPerPage) {
|
||||
returnArr.push(array.slice(i, i + this.props.itemsPerPage));
|
||||
}
|
||||
return returnArr;
|
||||
}
|
||||
}
|
||||
private chunkArray(array: CarouselItemProps[]): CarouselItemProps[][] {
|
||||
let returnArr = [] as CarouselItemProps[][];
|
||||
for (let i = 0; i < array.length; i += this.props.itemsPerPage) {
|
||||
returnArr.push(array.slice(i, i + this.props.itemsPerPage));
|
||||
}
|
||||
return returnArr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,30 @@
|
|||
import * as React from "react";
|
||||
import {Component} from "react";
|
||||
import { Component } from "react";
|
||||
|
||||
export interface CarouselItemProps {
|
||||
name: string;
|
||||
position: string;
|
||||
email: string;
|
||||
photo: string;
|
||||
name: string;
|
||||
position: string;
|
||||
email: string;
|
||||
photo: string;
|
||||
}
|
||||
interface CarouselItemState {}
|
||||
|
||||
export default class CarouselItem extends Component<CarouselItemProps, CarouselItemState> {
|
||||
constructor(props: CarouselItemProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
export default class CarouselItem extends Component<
|
||||
CarouselItemProps,
|
||||
CarouselItemState
|
||||
> {
|
||||
constructor(props: CarouselItemProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
public render() {
|
||||
return <div className="carousel-item">
|
||||
<img src={this.props.photo}></img>
|
||||
<div className="carousel-name">{this.props.name}</div>
|
||||
<div className="carousel-pos">{this.props.position}</div>
|
||||
</div>;
|
||||
}
|
||||
public render() {
|
||||
return (
|
||||
<div className="carousel-item">
|
||||
<img src={this.props.photo}></img>
|
||||
<div className="carousel-name">{this.props.name}</div>
|
||||
<div className="carousel-pos">{this.props.position}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,32 @@
|
|||
import * as React from "react";
|
||||
import {Component} from "react";
|
||||
import { Component } from "react";
|
||||
import CarouselItem, { CarouselItemProps } from "./CarouselItem";
|
||||
|
||||
interface CarouselPageProps {
|
||||
items: CarouselItemProps[];
|
||||
visible: boolean;
|
||||
items: CarouselItemProps[];
|
||||
visible: boolean;
|
||||
}
|
||||
interface CarouselPageState {}
|
||||
|
||||
export default class CarouselPage extends Component<CarouselPageProps, CarouselPageState> {
|
||||
constructor(props: CarouselPageProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
export default class CarouselPage extends Component<
|
||||
CarouselPageProps,
|
||||
CarouselPageState
|
||||
> {
|
||||
constructor(props: CarouselPageProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
public render() {
|
||||
return <div className="carousel-page" style={this.props.visible ? {} : {display: "none"}}>{
|
||||
this.props.items.map(n=>(
|
||||
<CarouselItem {...n}></CarouselItem>
|
||||
))
|
||||
}</div>;
|
||||
}
|
||||
public render() {
|
||||
return (
|
||||
<div
|
||||
className="carousel-page"
|
||||
style={this.props.visible ? {} : { display: "none" }}
|
||||
>
|
||||
{this.props.items.map((n) => (
|
||||
<CarouselItem {...n}></CarouselItem>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,37 @@
|
|||
import * as React from "react";
|
||||
import {Component} from "react";
|
||||
import { Component } from "react";
|
||||
|
||||
interface DefaultSectionProps {
|
||||
title: string;
|
||||
paragraphs?: string[];
|
||||
className?: string;
|
||||
title: string;
|
||||
paragraphs?: string[];
|
||||
className?: string;
|
||||
}
|
||||
interface DefaultSectionState {}
|
||||
|
||||
export default class DefaultSection extends Component<DefaultSectionProps, DefaultSectionState> {
|
||||
constructor(props: DefaultSectionProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
export default class DefaultSection extends Component<
|
||||
DefaultSectionProps,
|
||||
DefaultSectionState
|
||||
> {
|
||||
constructor(props: DefaultSectionProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
public render() {
|
||||
return <div className={`default-section ${this.props.className ? this.props.className : ""}`}>
|
||||
<div className="section-title">{this.props.title}</div>
|
||||
{(this.props.paragraphs ? this.props.paragraphs : []).map(n=>(
|
||||
<p>{n}</p>
|
||||
))}
|
||||
{this.props.children}
|
||||
</div>;
|
||||
}
|
||||
public render() {
|
||||
return (
|
||||
<div
|
||||
className={`default-section ${
|
||||
this.props.className ? this.props.className : ""
|
||||
}`}
|
||||
>
|
||||
<div className="section-title">{this.props.title}</div>
|
||||
{(this.props.paragraphs ? this.props.paragraphs : []).map(
|
||||
(n) => (
|
||||
<p>{n}</p>
|
||||
)
|
||||
)}
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,31 @@
|
|||
import * as React from "react";
|
||||
import {Component} from "react";
|
||||
import { Component } from "react";
|
||||
import SocialCard from "./SocialCard";
|
||||
import {EMAIL_WHITE, SOCIALS_WHITE} from "../Config";
|
||||
import { EMAIL_WHITE, SOCIALS_WHITE } from "../Config";
|
||||
|
||||
interface FooterProps {}
|
||||
interface FooterState {}
|
||||
|
||||
export default class Footer extends Component<FooterProps, FooterState> {
|
||||
constructor(props: FooterProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
constructor(props: FooterProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
public render() {
|
||||
return <div className="footer">
|
||||
<img src="img/logo_white.svg"></img>
|
||||
<div className="footer-scls">{
|
||||
[...EMAIL_WHITE, ...SOCIALS_WHITE].map(n => (
|
||||
<SocialCard url={n.url} image={n.icon} message={n.message}></SocialCard>
|
||||
))
|
||||
}</div>
|
||||
</div>;
|
||||
}
|
||||
public render() {
|
||||
return (
|
||||
<div className="footer">
|
||||
<img src="img/logo_white.svg"></img>
|
||||
<div className="footer-scls">
|
||||
{[...EMAIL_WHITE, ...SOCIALS_WHITE].map((n) => (
|
||||
<SocialCard
|
||||
url={n.url}
|
||||
image={n.icon}
|
||||
message={n.message}
|
||||
></SocialCard>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,36 @@
|
|||
import * as React from "react";
|
||||
import {Component} from "react";
|
||||
import { Component } from "react";
|
||||
|
||||
interface InvolveBoxProps {
|
||||
boxTitle: string;
|
||||
image: string;
|
||||
description: string;
|
||||
boxTitle: string;
|
||||
image: string;
|
||||
description: string;
|
||||
}
|
||||
interface InvolveBoxState {}
|
||||
|
||||
export default class InvolveBox extends Component<InvolveBoxProps, InvolveBoxState> {
|
||||
constructor(props: InvolveBoxProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
export default class InvolveBox extends Component<
|
||||
InvolveBoxProps,
|
||||
InvolveBoxState
|
||||
> {
|
||||
constructor(props: InvolveBoxProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
public render() {
|
||||
return <div className="involve-card">
|
||||
<a className="involve-title" href={"/"+this.props.boxTitle.toLowerCase()}>{this.props.boxTitle}</a>
|
||||
<img src={this.props.image}></img>
|
||||
<div className="involve-description">{this.props.description}</div>
|
||||
</div>;
|
||||
}
|
||||
public render() {
|
||||
return (
|
||||
<div className="involve-card">
|
||||
<a
|
||||
className="involve-title"
|
||||
href={"/" + this.props.boxTitle.toLowerCase()}
|
||||
>
|
||||
{this.props.boxTitle}
|
||||
</a>
|
||||
<img src={this.props.image}></img>
|
||||
<div className="involve-description">
|
||||
{this.props.description}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,28 @@
|
|||
import * as React from "react";
|
||||
import {Component} from "react";
|
||||
import { Component } from "react";
|
||||
|
||||
interface SocialCardProps {
|
||||
url: string;
|
||||
image: string;
|
||||
message: string;
|
||||
url: string;
|
||||
image: string;
|
||||
message: string;
|
||||
}
|
||||
interface SocialCardState {}
|
||||
|
||||
export default class SocialCard extends Component<SocialCardProps, SocialCardState> {
|
||||
constructor(props: SocialCardProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
export default class SocialCard extends Component<
|
||||
SocialCardProps,
|
||||
SocialCardState
|
||||
> {
|
||||
constructor(props: SocialCardProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
public render() {
|
||||
return <a href={this.props.url} className="social-card">
|
||||
<img src={this.props.image}></img>
|
||||
<div className="social-message">{this.props.message}</div>
|
||||
</a>;
|
||||
}
|
||||
public render() {
|
||||
return (
|
||||
<a href={this.props.url} className="social-card">
|
||||
<img src={this.props.image}></img>
|
||||
<div className="social-message">{this.props.message}</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,46 +1,56 @@
|
|||
import * as React from "react";
|
||||
import {Component} from "react";
|
||||
import {SOCIALS_WHITE} from "../Config";
|
||||
import { Component } from "react";
|
||||
import { SOCIALS_WHITE } from "../Config";
|
||||
|
||||
interface SplashProps {
|
||||
cta: string;
|
||||
backgrounds: string[];
|
||||
delay: number;
|
||||
cta: string;
|
||||
backgrounds: string[];
|
||||
delay: number;
|
||||
}
|
||||
interface SplashState {
|
||||
progress: number;
|
||||
progress: number;
|
||||
}
|
||||
|
||||
export default class Splash extends Component<SplashProps, SplashState> {
|
||||
private interval: number;
|
||||
constructor(props: SplashProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
progress: 0
|
||||
};
|
||||
this.interval = setInterval(this.changeImage.bind(this), this.props.delay) as any as number;
|
||||
}
|
||||
private interval: number;
|
||||
constructor(props: SplashProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
progress: 0,
|
||||
};
|
||||
this.interval = setInterval(
|
||||
this.changeImage.bind(this),
|
||||
this.props.delay
|
||||
) as any as number;
|
||||
}
|
||||
|
||||
private changeImage(): void {
|
||||
if (this.state.progress < this.props.backgrounds.length - 1) {
|
||||
this.setState({progress: this.state.progress + 1});
|
||||
} else {
|
||||
this.setState({progress: 0});
|
||||
}
|
||||
}
|
||||
private changeImage(): void {
|
||||
if (this.state.progress < this.props.backgrounds.length - 1) {
|
||||
this.setState({ progress: this.state.progress + 1 });
|
||||
} else {
|
||||
this.setState({ progress: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return <div className="splash" style={{
|
||||
backgroundImage: `url("${this.props.backgrounds[this.state.progress]}")`
|
||||
}}>
|
||||
<div className="call-to-action">{this.props.cta}</div>
|
||||
<div className="splash-socials">{
|
||||
SOCIALS_WHITE.map(n=>(
|
||||
<a href={n.url} key={n.icon}>
|
||||
<img src={n.icon}></img>
|
||||
</a>
|
||||
))
|
||||
}</div>
|
||||
</div>;
|
||||
}
|
||||
public render() {
|
||||
return (
|
||||
<div
|
||||
className="splash"
|
||||
style={{
|
||||
backgroundImage: `url("${
|
||||
this.props.backgrounds[this.state.progress]
|
||||
}")`,
|
||||
}}
|
||||
>
|
||||
<div className="call-to-action">{this.props.cta}</div>
|
||||
<div className="splash-socials">
|
||||
{SOCIALS_WHITE.map((n) => (
|
||||
<a href={n.url} key={n.icon}>
|
||||
<img src={n.icon}></img>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,59 +1,82 @@
|
|||
import * as React from "react";
|
||||
import {Component} from "react";
|
||||
import { Component } from "react";
|
||||
|
||||
interface TopBarProps {
|
||||
links: {
|
||||
name: string;
|
||||
url: string;
|
||||
}[];
|
||||
links: {
|
||||
name: string;
|
||||
url: string;
|
||||
}[];
|
||||
}
|
||||
interface TopBarState {
|
||||
showBurger: boolean;
|
||||
menuVisible: boolean;
|
||||
showBurger: boolean;
|
||||
menuVisible: boolean;
|
||||
}
|
||||
|
||||
export default class TopBar extends Component<TopBarProps, TopBarState> {
|
||||
private static HAMBURGER_POINT = 1290;
|
||||
constructor(props: TopBarProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showBurger: this.shouldShowBurger(),
|
||||
menuVisible: false
|
||||
};
|
||||
window.addEventListener("resize", this.checkResize.bind(this));
|
||||
}
|
||||
private toggleMenu() {
|
||||
this.setState({menuVisible: !this.state.menuVisible});
|
||||
}
|
||||
private checkResize() {
|
||||
this.setState({showBurger: this.shouldShowBurger()});
|
||||
}
|
||||
private shouldShowBurger(): boolean {
|
||||
return window.innerWidth < TopBar.HAMBURGER_POINT;
|
||||
}
|
||||
private static HAMBURGER_POINT = 1290;
|
||||
constructor(props: TopBarProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showBurger: this.shouldShowBurger(),
|
||||
menuVisible: false,
|
||||
};
|
||||
window.addEventListener("resize", this.checkResize.bind(this));
|
||||
}
|
||||
private toggleMenu() {
|
||||
this.setState({ menuVisible: !this.state.menuVisible });
|
||||
}
|
||||
private checkResize() {
|
||||
this.setState({ showBurger: this.shouldShowBurger() });
|
||||
}
|
||||
private shouldShowBurger(): boolean {
|
||||
return window.innerWidth < TopBar.HAMBURGER_POINT;
|
||||
}
|
||||
|
||||
public render() {
|
||||
return <div className={`topbar${
|
||||
this.state.showBurger ? " burger-bar" : ""
|
||||
}`}>
|
||||
<div className="img-cont">
|
||||
<a href="/"><img src="img/logo.svg"></img></a>
|
||||
<div className="burger" style={{
|
||||
display: this.state.showBurger ? "initial" : "none"
|
||||
}} onClick={this.toggleMenu.bind(this)} role="menubar">≡</div>
|
||||
</div>
|
||||
<div className="links">
|
||||
|
||||
<div className={`link-items${
|
||||
this.state.showBurger ? " burger-time" : ""
|
||||
}`} style={{display: this.state.showBurger ?
|
||||
(this.state.menuVisible ? "flex" : "none") :
|
||||
"initial"}}>{
|
||||
this.props.links.map(l=>{
|
||||
return <a className="navlink" href={l.url}>{l.name}</a>
|
||||
})
|
||||
}</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
public render() {
|
||||
return (
|
||||
<div
|
||||
className={`topbar${
|
||||
this.state.showBurger ? " burger-bar" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="img-cont">
|
||||
<a href="/">
|
||||
<img src="img/logo.svg"></img>
|
||||
</a>
|
||||
<div
|
||||
className="burger"
|
||||
style={{
|
||||
display: this.state.showBurger ? "initial" : "none",
|
||||
}}
|
||||
onClick={this.toggleMenu.bind(this)}
|
||||
role="menubar"
|
||||
>
|
||||
≡
|
||||
</div>
|
||||
</div>
|
||||
<div className="links">
|
||||
<div
|
||||
className={`link-items${
|
||||
this.state.showBurger ? " burger-time" : ""
|
||||
}`}
|
||||
style={{
|
||||
display: this.state.showBurger
|
||||
? this.state.menuVisible
|
||||
? "flex"
|
||||
: "none"
|
||||
: "initial",
|
||||
}}
|
||||
>
|
||||
{this.props.links.map((l) => {
|
||||
return (
|
||||
<a className="navlink" href={l.url}>
|
||||
{l.name}
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
url('../fonts/roboto-v29-latin-700.woff') format('woff');
|
||||
/* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* montserrat-regular - latin */
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
|
|
|
@ -1,29 +1,34 @@
|
|||
/* Color vars should go here */
|
||||
/* Global CSS Properties */
|
||||
:root {
|
||||
--main: #00629B;
|
||||
--accent: #FFCD00;
|
||||
--secondary: white;
|
||||
--dark: #2A1A4E;
|
||||
--main: #00629b;
|
||||
--accent: #ffcd00;
|
||||
--secondary: #ffffff;
|
||||
--dark: #2a1a4e;
|
||||
}
|
||||
|
||||
* {
|
||||
scrollbar-width: auto;
|
||||
scrollbar-color: var(--main) transparent;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 0.6em;
|
||||
height: 0.6em;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: var(--main);
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -41,6 +46,7 @@ a:hover {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.topbar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@ -50,20 +56,24 @@ a:hover {
|
|||
padding: 0.5em;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.topbar.burger-bar {
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
.topbar.burger-bar > .img-cont {
|
||||
|
||||
.topbar.burger-bar>.img-cont {
|
||||
align-self: flex-start;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.img-cont img {
|
||||
width: 22em;
|
||||
}
|
||||
|
||||
.burger {
|
||||
font-size: 2.8em;
|
||||
font-weight: normal;
|
||||
|
@ -71,26 +81,31 @@ a:hover {
|
|||
cursor: pointer;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
.link-items.burger-time {
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
background-color: var(--secondary);
|
||||
width: 10em;
|
||||
box-shadow: rgba(0,0,0,0.3) 4px 4px 3px 0px;
|
||||
box-shadow: rgba(0, 0, 0, 0.3) 4px 4px 3px 0px;
|
||||
right: 0;
|
||||
margin-top: 0.36em;
|
||||
}
|
||||
.link-items.burger-time > a {
|
||||
|
||||
.link-items.burger-time>a {
|
||||
margin: 0;
|
||||
padding: 0.4em 0.75em;
|
||||
}
|
||||
.link-items.burger-time > a:hover {
|
||||
background-color: rgba(0,0,0,0.1);
|
||||
|
||||
.link-items.burger-time>a:hover {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.navlink {
|
||||
font-size: 1.1em;
|
||||
margin: 0.75em;
|
||||
}
|
||||
|
||||
.splash {
|
||||
background-color: var(--main);
|
||||
background-blend-mode: overlay;
|
||||
|
@ -104,6 +119,7 @@ a:hover {
|
|||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.call-to-action {
|
||||
font-size: 3.2em;
|
||||
width: 11em;
|
||||
|
@ -111,16 +127,20 @@ a:hover {
|
|||
text-align: center;
|
||||
font-family: "Montserrat";
|
||||
}
|
||||
|
||||
.splash-socials {
|
||||
margin-top: 2em;
|
||||
}
|
||||
.splash-socials > a {
|
||||
|
||||
.splash-socials>a {
|
||||
margin-left: 1.5em;
|
||||
margin-right: 1.5em;
|
||||
}
|
||||
|
||||
.splash-socials img {
|
||||
width: 4.7em;
|
||||
}
|
||||
|
||||
.default-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -131,28 +151,34 @@ a:hover {
|
|||
padding-top: 2em;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 3em;
|
||||
margin-top: 0.8em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
.default-section > p, .project-space > p {
|
||||
|
||||
.default-section>p,
|
||||
.project-space>p {
|
||||
font-size: 1.8em;
|
||||
width: 22em;
|
||||
color: var(--dark);
|
||||
text-align: center;
|
||||
}
|
||||
.project-space > p {
|
||||
|
||||
.project-space>p {
|
||||
color: var(--secondary);
|
||||
}
|
||||
.default-section > p:first-child {
|
||||
|
||||
.default-section>p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.project-space {
|
||||
border-radius: 50% 50% 0 0 / 4rem;
|
||||
background-color: var(--main);
|
||||
background-blend-mode: overlay;
|
||||
background-image: url("../img/backgrounds/ps.png");
|
||||
background-image: url("../img/backgrounds/ps.webp");
|
||||
height: 40em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
@ -161,32 +187,39 @@ a:hover {
|
|||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.project-space a {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.ps-title {
|
||||
font-size: 3em;
|
||||
color: white;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.visit-us {
|
||||
font-size: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.ex-link {
|
||||
color: var(--accent);
|
||||
font-size: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.involved {
|
||||
border-radius: 50% 50% 0 0 / 4rem;
|
||||
transform: translateY(-4rem);
|
||||
}
|
||||
|
||||
.cards {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.involve-card {
|
||||
padding: 1.75em 2.5em;
|
||||
border: var(--main) solid 0.37em;
|
||||
|
@ -198,32 +231,41 @@ a:hover {
|
|||
flex-direction: column;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.involve-title {
|
||||
font-size: 3em;
|
||||
color: var(--main);
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.involve-card > img {
|
||||
|
||||
.involve-card>img {
|
||||
width: 20em;
|
||||
margin-bottom: 1em;
|
||||
border-radius: 0.2em;
|
||||
}
|
||||
|
||||
.links {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.social-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 1em;
|
||||
width: 25em;
|
||||
}
|
||||
.social-card > img {
|
||||
|
||||
.social-card>img {
|
||||
width: 4.7em;
|
||||
}
|
||||
|
||||
.social-message {
|
||||
font-size: 2em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
.join-scls, .footer-scls {
|
||||
|
||||
.join-scls,
|
||||
.footer-scls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-width: 55em;
|
||||
|
@ -231,6 +273,7 @@ a:hover {
|
|||
flex-wrap: wrap;
|
||||
margin-left: 5em;
|
||||
}
|
||||
|
||||
.carousel-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -240,45 +283,58 @@ a:hover {
|
|||
border-radius: 1em;
|
||||
margin: 1em;
|
||||
}
|
||||
.carousel-item > img {
|
||||
|
||||
.carousel-item>img {
|
||||
width: 18.7em;
|
||||
border-radius: 0.2em;
|
||||
}
|
||||
|
||||
.carousel-name {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.carousel-pos {
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
.carousel-email {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.carousel-page {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.carousel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.carousel-left {
|
||||
margin-left: 2em;
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
.carousel-right, .carousel-left {
|
||||
|
||||
.carousel-right,
|
||||
.carousel-left {
|
||||
cursor: pointer;
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
.carousel-right {
|
||||
margin-right: 2em;
|
||||
}
|
||||
|
||||
.contact {
|
||||
border-radius: 0 0 50% 50% / 4rem;
|
||||
transform: translateY(4rem);
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: var(--main);
|
||||
padding-top: 7em;
|
||||
|
@ -288,18 +344,22 @@ a:hover {
|
|||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.footer-scls {
|
||||
max-width: 30em;
|
||||
font-size: 0.6em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.footer-scls img {
|
||||
width: 4.7em;
|
||||
}
|
||||
.footer > img {
|
||||
|
||||
.footer>img {
|
||||
width: 30em;
|
||||
}
|
||||
|
||||
|
@ -309,27 +369,33 @@ a:hover {
|
|||
font-size: 0.85em;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 650px) {
|
||||
html {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.footer > img{
|
||||
|
||||
.footer>img {
|
||||
width: 15em;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 540px) {
|
||||
html {
|
||||
font-size: 0.6em;
|
||||
}
|
||||
|
||||
.footer>img {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 420px) {
|
||||
html {
|
||||
font-size: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 320px) {
|
||||
html {
|
||||
font-size: 0.45em;
|
||||
|
@ -337,9 +403,9 @@ a:hover {
|
|||
}
|
||||
|
||||
#cal {
|
||||
border:solid 1px #777;
|
||||
border: solid 1px #777;
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
#calendar {
|
||||
|
@ -354,6 +420,6 @@ a:hover {
|
|||
|
||||
iframe {
|
||||
width: 80vw;
|
||||
height: 600px;
|
||||
max-width: 1000px;
|
||||
height: 600px;
|
||||
max-width: 1000px;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import * as ReactDom from "react-dom";
|
||||
import * as React from "react";
|
||||
import TopBar from "./components/TopBar";
|
||||
import {ACTIVE_PAGES, SOCIALS, EMAIL, OFFICERS} from "./Config";
|
||||
import { ACTIVE_PAGES, SOCIALS, EMAIL, OFFICERS } from "./Config";
|
||||
import Splash from "./components/Splash";
|
||||
import DefaultSection from "./components/DefaultSection";
|
||||
import InvolveBox from "./components/InvolveBox";
|
||||
|
@ -13,38 +13,59 @@ interface MainProps {}
|
|||
interface MainState {}
|
||||
|
||||
class Main extends React.Component<MainProps, MainState> {
|
||||
constructor(props: MainProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
public render() {
|
||||
return <>
|
||||
<TopBar links={ACTIVE_PAGES}></TopBar>
|
||||
<Splash cta="Come out to our events!" delay={2000} backgrounds={["img/backgrounds/fa21social.png"]}></Splash>
|
||||
constructor(props: MainProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
public render() {
|
||||
return (
|
||||
<>
|
||||
<TopBar links={ACTIVE_PAGES}></TopBar>
|
||||
<Splash
|
||||
cta="Come out to our events!"
|
||||
delay={2000}
|
||||
backgrounds={["img/backgrounds/fa21social.webp"]}
|
||||
></Splash>
|
||||
|
||||
<DefaultSection title="Events">
|
||||
<iframe src="https://calendar.google.com/calendar/embed?src=666sh64sku5n29qv2a2f4598jc%40group.calendar.google.com&ctz=America%2FLos_Angeles" frameBorder="0" scrolling="no"></iframe>
|
||||
</DefaultSection>
|
||||
<DefaultSection title="Events">
|
||||
<iframe
|
||||
src="https://calendar.google.com/calendar/embed?src=666sh64sku5n29qv2a2f4598jc%40group.calendar.google.com&ctz=America%2FLos_Angeles"
|
||||
frameBorder="0"
|
||||
scrolling="no"
|
||||
></iframe>
|
||||
</DefaultSection>
|
||||
|
||||
<DefaultSection title="Open Access Hours">
|
||||
<iframe src="https://calendar.google.com/calendar/embed?src=c_gr3iim9ae4dv9784qkf8meb40c%40group.calendar.google.com&ctz=America%2FLos_Angeles" frameBorder="0" scrolling="no"></iframe>
|
||||
</DefaultSection>
|
||||
<DefaultSection title="Open Access Hours">
|
||||
<iframe
|
||||
src="https://calendar.google.com/calendar/embed?src=c_gr3iim9ae4dv9784qkf8meb40c%40group.calendar.google.com&ctz=America%2FLos_Angeles"
|
||||
frameBorder="0"
|
||||
scrolling="no"
|
||||
></iframe>
|
||||
</DefaultSection>
|
||||
|
||||
<div id="contact-us">
|
||||
<DefaultSection className="contact" title="Have questions? Contact us!">
|
||||
<div className="join-scls">{
|
||||
[...EMAIL, ...SOCIALS].map(n => (
|
||||
<SocialCard url={n.url} image={n.icon} message={n.message}></SocialCard>
|
||||
))
|
||||
}</div>
|
||||
</DefaultSection>
|
||||
</div>
|
||||
<div id="contact-us">
|
||||
<DefaultSection
|
||||
className="contact"
|
||||
title="Have questions? Contact us!"
|
||||
>
|
||||
<div className="join-scls">
|
||||
{[...EMAIL, ...SOCIALS].map((n) => (
|
||||
<SocialCard
|
||||
url={n.url}
|
||||
image={n.icon}
|
||||
message={n.message}
|
||||
></SocialCard>
|
||||
))}
|
||||
</div>
|
||||
</DefaultSection>
|
||||
</div>
|
||||
|
||||
<Footer></Footer>
|
||||
</>;
|
||||
}
|
||||
<Footer></Footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDom.render(<Main></Main>, document.getElementById("root"));
|
||||
|
||||
export default {};
|
||||
export default {};
|
||||
|
|
Before Width: | Height: | Size: 147 KiB |
BIN
src/public/img/backgrounds/committee.webp
Normal file
After Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 915 KiB |
BIN
src/public/img/backgrounds/fa21qp.webp
Normal file
After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 449 KiB |
BIN
src/public/img/backgrounds/fa21social.webp
Normal file
After Width: | Height: | Size: 275 KiB |
Before Width: | Height: | Size: 184 KiB |
BIN
src/public/img/backgrounds/gbm.webp
Normal file
After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 11 MiB |
BIN
src/public/img/backgrounds/micromouse.webp
Normal file
After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 771 KiB |
BIN
src/public/img/backgrounds/ps.webp
Normal file
After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 2 MiB |
BIN
src/public/img/backgrounds/robocar.webp
Normal file
After Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 24 MiB |
Before Width: | Height: | Size: 250 KiB |
BIN
src/public/img/backgrounds/robofest.webp
Normal file
After Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 16 MiB |
BIN
src/public/img/backgrounds/sp22qp.webp
Normal file
After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 896 KiB |
BIN
src/public/img/committees/outreach.webp
Normal file
After Width: | Height: | Size: 129 KiB |
Before Width: | Height: | Size: 897 KiB |
BIN
src/public/img/committees/pr.webp
Normal file
After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 167 KiB |
BIN
src/public/img/committees/professional.webp
Normal file
After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 480 KiB |
BIN
src/public/img/committees/social.webp
Normal file
After Width: | Height: | Size: 115 KiB |
Before Width: | Height: | Size: 356 KiB |
BIN
src/public/img/committees/technical.webp
Normal file
After Width: | Height: | Size: 62 KiB |
|
@ -1,7 +1,7 @@
|
|||
import * as ReactDom from "react-dom";
|
||||
import * as React from "react";
|
||||
import TopBar from "./components/TopBar";
|
||||
import {ACTIVE_PAGES, SOCIALS, EMAIL, OFFICERS} from "./Config";
|
||||
import { ACTIVE_PAGES, SOCIALS, EMAIL, OFFICERS } from "./Config";
|
||||
import Splash from "./components/Splash";
|
||||
import DefaultSection from "./components/DefaultSection";
|
||||
import InvolveBox from "./components/InvolveBox";
|
||||
|
@ -13,50 +13,97 @@ interface MainProps {}
|
|||
interface MainState {}
|
||||
|
||||
class Main extends React.Component<MainProps, MainState> {
|
||||
constructor(props: MainProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
public render() {
|
||||
return <>
|
||||
<TopBar links={ACTIVE_PAGES}></TopBar>
|
||||
<Splash cta="Join the 2nd largest IEEE student branch in the US!" delay={2000} backgrounds={["img/backgrounds/fa21qp.png"]}></Splash>
|
||||
<div id="about-us">
|
||||
<DefaultSection title="We are..." paragraphs={[
|
||||
"A diverse engineering community seeking to empower students through hands-on projects, networking opportunities, and social events. Bonus points on having an open-access project studio!",
|
||||
"The Institute of Electrical and Electronics Engineers (IEEE) UC San Diego student branch is the second largest student chapter in the world’s largest professional organization. On the student level, we provide members with a plethora of ways to get involved!"
|
||||
]}></DefaultSection>
|
||||
</div>
|
||||
<div className="project-space">
|
||||
<div className="ps-title">Join us at the Project Space!</div>
|
||||
<p>The <a href="https://www.google.com/maps/@32.8817126,-117.2350998,59m/">IEEE Project Space</a> is an open-access, collaborative space where students can do homework or get access to basic electronic tools such as soldering stations, breadboard components, and Arduino and Raspberry PI parts!</p>
|
||||
<a className="visit-us" href="https://www.google.com/maps/@32.8817126,-117.2350998,59m/">Come visit at EBU1-4710!</a>
|
||||
</div>
|
||||
<DefaultSection className={"involved"} title="How else can I get involved?">
|
||||
<div className="cards">
|
||||
<InvolveBox boxTitle="Events" image="img/backgrounds/fa21social.png" description="Meet fellow IEEE members!"></InvolveBox>
|
||||
<InvolveBox boxTitle="Projects" image="img/backgrounds/robofest.png" description="Learn new skills!"></InvolveBox>
|
||||
<InvolveBox boxTitle="Committees" image="img/backgrounds/gbm.png" description="Build our amazing community!"></InvolveBox>
|
||||
</div>
|
||||
</DefaultSection>
|
||||
|
||||
<div id="contact-us">
|
||||
<DefaultSection title="Have questions? Contact us!">
|
||||
<div className="join-scls">{
|
||||
[...EMAIL, ...SOCIALS].map(n => (
|
||||
<SocialCard url={n.url} image={n.icon} message={n.message}></SocialCard>
|
||||
))
|
||||
}</div>
|
||||
</DefaultSection>
|
||||
<DefaultSection className="contact" title="Or... Contact one of our staff!">
|
||||
<Carousel items={OFFICERS} itemsPerPage={6}></Carousel>
|
||||
</DefaultSection>
|
||||
</div>
|
||||
<Footer></Footer>
|
||||
</>;
|
||||
}
|
||||
constructor(props: MainProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
public render() {
|
||||
return (
|
||||
<>
|
||||
<TopBar links={ACTIVE_PAGES}></TopBar>
|
||||
<Splash
|
||||
cta="Join the 2nd largest IEEE student branch in the US!"
|
||||
delay={2000}
|
||||
backgrounds={["img/backgrounds/fa21qp.webp"]}
|
||||
></Splash>
|
||||
<div id="about-us">
|
||||
<DefaultSection
|
||||
title="We are..."
|
||||
paragraphs={[
|
||||
"A diverse engineering community seeking to empower students through hands-on projects, networking opportunities, and social events. Bonus points on having an open-access project studio!",
|
||||
"The Institute of Electrical and Electronics Engineers (IEEE) UC San Diego student branch is the second largest student chapter in the world’s largest professional organization. On the student level, we provide members with a plethora of ways to get involved!",
|
||||
]}
|
||||
></DefaultSection>
|
||||
</div>
|
||||
<div className="project-space">
|
||||
<div className="ps-title">
|
||||
Join us at the Project Space!
|
||||
</div>
|
||||
<p>
|
||||
The{" "}
|
||||
<a href="https://www.google.com/maps/@32.8817126,-117.2350998,59m/">
|
||||
IEEE Project Space
|
||||
</a>{" "}
|
||||
is an open-access, collaborative space where students
|
||||
can do homework or get access to basic electronic tools
|
||||
such as soldering stations, breadboard components, and
|
||||
Arduino and Raspberry PI parts!
|
||||
</p>
|
||||
<a
|
||||
className="visit-us"
|
||||
href="https://www.google.com/maps/@32.8817126,-117.2350998,59m/"
|
||||
>
|
||||
Come visit at EBU1-4710!
|
||||
</a>
|
||||
</div>
|
||||
<DefaultSection
|
||||
className={"involved"}
|
||||
title="How else can I get involved?"
|
||||
>
|
||||
<div className="cards">
|
||||
<InvolveBox
|
||||
boxTitle="Events"
|
||||
image="img/backgrounds/fa21social.webp"
|
||||
description="Meet fellow IEEE members!"
|
||||
></InvolveBox>
|
||||
<InvolveBox
|
||||
boxTitle="Projects"
|
||||
image="img/backgrounds/robofest.webp"
|
||||
description="Learn new skills!"
|
||||
></InvolveBox>
|
||||
<InvolveBox
|
||||
boxTitle="Committees"
|
||||
image="img/backgrounds/gbm.webp"
|
||||
description="Build our amazing community!"
|
||||
></InvolveBox>
|
||||
</div>
|
||||
</DefaultSection>
|
||||
|
||||
<div id="contact-us">
|
||||
<DefaultSection title="Have questions? Contact us!">
|
||||
<div className="join-scls">
|
||||
{[...EMAIL, ...SOCIALS].map((n) => (
|
||||
<SocialCard
|
||||
url={n.url}
|
||||
image={n.icon}
|
||||
message={n.message}
|
||||
></SocialCard>
|
||||
))}
|
||||
</div>
|
||||
</DefaultSection>
|
||||
<DefaultSection
|
||||
className="contact"
|
||||
title="Or... Contact one of our staff!"
|
||||
>
|
||||
<Carousel items={OFFICERS} itemsPerPage={6}></Carousel>
|
||||
</DefaultSection>
|
||||
</div>
|
||||
<Footer></Footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDom.render(<Main></Main>, document.getElementById("root"));
|
||||
|
||||
export default {};
|
||||
export default {};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as ReactDom from "react-dom";
|
||||
import * as React from "react";
|
||||
import TopBar from "./components/TopBar";
|
||||
import {ACTIVE_PAGES, SOCIALS, EMAIL, OFFICERS} from "./Config";
|
||||
import { ACTIVE_PAGES, SOCIALS, EMAIL, OFFICERS } from "./Config";
|
||||
import Splash from "./components/Splash";
|
||||
import DefaultSection from "./components/DefaultSection";
|
||||
import InvolveBox from "./components/InvolveBox";
|
||||
|
@ -13,48 +13,86 @@ interface MainProps {}
|
|||
interface MainState {}
|
||||
|
||||
class Main extends React.Component<MainProps, MainState> {
|
||||
constructor(props: MainProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
public render() {
|
||||
return <>
|
||||
<TopBar links={ACTIVE_PAGES}></TopBar>
|
||||
<Splash cta="Gain hands-on experience to make your resume stand out! No experience required!" delay={2000} backgrounds={["img/backgrounds/robocar.png"]}></Splash>
|
||||
<DefaultSection title="RoboCup" paragraphs={[
|
||||
"\"RoboCup is an international scientific initiative with the goal to advance the state of the art of intelligent robots. When established in 1997, the original mission was to field a team of robots capable of winning against the human soccer World Cup champions by 2050.\""
|
||||
,"IEEE hosts Robocup Soccer, an annual project where teams develop six robots to compete with other teams during Robofest. Join this hands-on project to explore computer vision, mechanical design, and microcontroller development!"
|
||||
]}>
|
||||
<a className='ex-link' href='https://www.robocup.org/'>RoboCup website</a>
|
||||
</DefaultSection>
|
||||
<DefaultSection title="Quarterly Projects" paragraphs={[
|
||||
"Getting started on hardware development or want to make your own project? IEEE's Quarterly Projects aims to provide students with project experience in a span of 10 weeks. During QP, students will acquire skills used in the industry such as C++ and the prototyping process with the assistance of our mentors."
|
||||
]}>
|
||||
<a className='ex-link' href='https://forms.gle/eW6e1i3vWCdBj7Vn6'>Apply here</a>
|
||||
</DefaultSection>
|
||||
<DefaultSection className={"past-proj"} title="Past Projects">
|
||||
<div className="cards">
|
||||
<InvolveBox boxTitle="" image="img/backgrounds/robocar.png" description="'22 Robocar Competition"></InvolveBox>
|
||||
<InvolveBox boxTitle="" image="img/backgrounds/micromouse.png" description="'22 Micromouse Competition"></InvolveBox>
|
||||
<InvolveBox boxTitle="" image="img/backgrounds/sp22qp.png" description="'22 Spring QP Showcase"></InvolveBox>
|
||||
</div>
|
||||
</DefaultSection>
|
||||
constructor(props: MainProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
public render() {
|
||||
return (
|
||||
<>
|
||||
<TopBar links={ACTIVE_PAGES}></TopBar>
|
||||
<Splash
|
||||
cta="Gain hands-on experience to make your resume stand out! No experience required!"
|
||||
delay={2000}
|
||||
backgrounds={["img/backgrounds/robocar.webp"]}
|
||||
></Splash>
|
||||
<DefaultSection
|
||||
title="RoboCup"
|
||||
paragraphs={[
|
||||
'"RoboCup is an international scientific initiative with the goal to advance the state of the art of intelligent robots. When established in 1997, the original mission was to field a team of robots capable of winning against the human soccer World Cup champions by 2050."',
|
||||
"IEEE hosts Robocup Soccer, an annual project where teams develop six robots to compete with other teams during Robofest. Join this hands-on project to explore computer vision, mechanical design, and microcontroller development!",
|
||||
]}
|
||||
>
|
||||
<a className="ex-link" href="https://www.robocup.org/">
|
||||
RoboCup website
|
||||
</a>
|
||||
</DefaultSection>
|
||||
<DefaultSection
|
||||
title="Quarterly Projects"
|
||||
paragraphs={[
|
||||
"Getting started on hardware development or want to make your own project? IEEE's Quarterly Projects aims to provide students with project experience in a span of 10 weeks. During QP, students will acquire skills used in the industry such as C++ and the prototyping process with the assistance of our mentors.",
|
||||
]}
|
||||
>
|
||||
<a
|
||||
className="ex-link"
|
||||
href="https://forms.gle/eW6e1i3vWCdBj7Vn6"
|
||||
>
|
||||
Apply here
|
||||
</a>
|
||||
</DefaultSection>
|
||||
<DefaultSection className={"past-proj"} title="Past Projects">
|
||||
<div className="cards">
|
||||
<InvolveBox
|
||||
boxTitle=""
|
||||
image="img/backgrounds/robocar.webp"
|
||||
description="'22 Robocar Competition"
|
||||
></InvolveBox>
|
||||
<InvolveBox
|
||||
boxTitle=""
|
||||
image="img/backgrounds/micromouse.webp"
|
||||
description="'22 Micromouse Competition"
|
||||
></InvolveBox>
|
||||
<InvolveBox
|
||||
boxTitle=""
|
||||
image="img/backgrounds/sp22qp.webp"
|
||||
description="'22 Spring QP Showcase"
|
||||
></InvolveBox>
|
||||
</div>
|
||||
</DefaultSection>
|
||||
|
||||
<div id="contact-us">
|
||||
<DefaultSection className="contact" title="Have questions? Contact us!">
|
||||
<div className="join-scls">{
|
||||
[...EMAIL, ...SOCIALS].map(n => (
|
||||
<SocialCard url={n.url} image={n.icon} message={n.message}></SocialCard>
|
||||
))
|
||||
}</div>
|
||||
</DefaultSection>
|
||||
</div>
|
||||
<div id="contact-us">
|
||||
<DefaultSection
|
||||
className="contact"
|
||||
title="Have questions? Contact us!"
|
||||
>
|
||||
<div className="join-scls">
|
||||
{[...EMAIL, ...SOCIALS].map((n) => (
|
||||
<SocialCard
|
||||
url={n.url}
|
||||
image={n.icon}
|
||||
message={n.message}
|
||||
></SocialCard>
|
||||
))}
|
||||
</div>
|
||||
</DefaultSection>
|
||||
</div>
|
||||
|
||||
<Footer></Footer>
|
||||
</>;
|
||||
}
|
||||
<Footer></Footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDom.render(<Main></Main>, document.getElementById("root"));
|
||||
|
||||
export default {};
|
||||
export default {};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>$TITLE</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/fonts.css" media="screen">
|
||||
|
@ -9,8 +10,10 @@
|
|||
<meta name="description" content="$DESCRIPTION">
|
||||
<meta name="theme-color" content="$THEMECOLOR">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="text/javascript" src="$JSFILE"></script>
|
||||
<div id="root"></div>
|
||||
<script type="text/javascript" src="$JSFILE"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,73 +1,78 @@
|
|||
const path = require("path");
|
||||
const CopyPlugin = require("copy-webpack-plugin");
|
||||
const LicensePlugin = require("license-webpack-plugin").LicenseWebpackPlugin;
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const fs = require("fs");
|
||||
|
||||
module.exports = {
|
||||
entry: loadEntries("./src/public"),
|
||||
output: {
|
||||
path: path.resolve(__dirname, "build/public"),
|
||||
filename: "./js/[name].js"
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.(tsx|ts)$/,
|
||||
use: "ts-loader"
|
||||
},
|
||||
{
|
||||
test: /\.(js)$/,
|
||||
use: "babel-loader"
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ["style-loader", "css-loader"]
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".tsx", ".ts", ".js"],
|
||||
},
|
||||
mode: "production",
|
||||
plugins: [
|
||||
new CopyPlugin({
|
||||
patterns: [{
|
||||
from: "./src/public",
|
||||
to: ".",
|
||||
globOptions: {
|
||||
ignore: ["**/*.tsx", "**/*.ts"]
|
||||
}
|
||||
}
|
||||
],
|
||||
}),
|
||||
new LicensePlugin({
|
||||
outputFilename: 'third-party-notice.txt'
|
||||
})
|
||||
],
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
output: {
|
||||
comments: false,
|
||||
},
|
||||
},
|
||||
extractComments: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
//devtool: "source-map"
|
||||
entry: loadEntries("./src/public"),
|
||||
output: {
|
||||
path: path.resolve(__dirname, "build/public"),
|
||||
filename: "./js/[name].js",
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(tsx|ts)$/,
|
||||
use: "ts-loader",
|
||||
},
|
||||
{
|
||||
test: /\.(js)$/,
|
||||
use: "babel-loader",
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ["style-loader", "css-loader"],
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".tsx", ".ts", ".js"],
|
||||
},
|
||||
mode: "production",
|
||||
plugins: [
|
||||
new CopyPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: "./src/public",
|
||||
to: ".",
|
||||
globOptions: {
|
||||
ignore: ["**/*.tsx", "**/*.ts"],
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
new LicensePlugin({
|
||||
outputFilename: "third-party-notice.txt",
|
||||
}),
|
||||
],
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
output: {
|
||||
comments: false,
|
||||
},
|
||||
},
|
||||
extractComments: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
//devtool: "source-map"
|
||||
devServer: {
|
||||
static: "./build/public",
|
||||
},
|
||||
};
|
||||
|
||||
function loadEntries(dir) {
|
||||
let files = fs.readdirSync(path.join(__dirname, dir));
|
||||
let entries = {};
|
||||
files.forEach(file => {
|
||||
let name = file.match(/^(.*)\.tsx$/);
|
||||
if (name) {
|
||||
entries[name[1]] = path.join(__dirname, dir, file);
|
||||
}
|
||||
});
|
||||
return entries;
|
||||
}
|
||||
let files = fs.readdirSync(path.join(__dirname, dir));
|
||||
let entries = {};
|
||||
files.forEach((file) => {
|
||||
let name = file.match(/^(.*)\.tsx$/);
|
||||
if (name) {
|
||||
entries[name[1]] = path.join(__dirname, dir, file);
|
||||
}
|
||||
});
|
||||
return entries;
|
||||
}
|
||||
|
|