22 |
24 |
25 | ## Description
26 |
27 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
28 |
29 | ## Installation
30 |
31 | ```bash
32 | $ npm install
33 | ```
34 |
35 | ## Running the app
36 |
37 | ```bash
38 | # development
39 | $ npm run start
40 |
41 | # watch mode
42 | $ npm run start:dev
43 |
44 | # production mode
45 | $ npm run start:prod
46 | ```
47 |
48 | ## Test
49 |
50 | ```bash
51 | # unit tests
52 | $ npm run test
53 |
54 | # e2e tests
55 | $ npm run test:e2e
56 |
57 | # test coverage
58 | $ npm run test:cov
59 | ```
60 |
61 | ## Support
62 |
63 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
64 |
65 | ## Stay in touch
66 |
67 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
68 | - Website - [https://nestjs.com](https://nestjs.com/)
69 | - Twitter - [@nestframework](https://twitter.com/nestframework)
70 |
71 | ## License
72 |
73 | Nest is [MIT licensed](LICENSE).
74 |
--------------------------------------------------------------------------------
/database/contraceptives.csv:
--------------------------------------------------------------------------------
1 | "Timestamp","Username","Name of Contraceptive","Description of Contraceptive","Accessibility","How to use it?","Tags (separate with commas)","Care-free for","Unit for previous question","Lasts up to","Unit for previous question","What if I missed once in the routine or made any mistake?","% Effectiveness","When it starts to work?","How Can I stop it?(When)","How Can I stop it(How)","How Can I Stop it(How Long)","Getting Back to Fertility ","Non-contraceptive benefits","Side effects","How it works?","Health Risk","This form of birth control may not be suitable if you","Warning","Where to access","Who will administer this method?","Cost (lower bound in $)","Cost (upper bound in $)","Additional Cost Information","Things to notice about this method"
2 | "2022/03/01 8:01:06 AM EST","heyman.b@husky.neu.edu","implant","The implant is a tiny, flexible rod (the size of a matchstick) that is inserted under the skin of your upper arm to prevent pregnancy. It is a long-acting hormonal methods.","Operation by doctor","It is inserted by a doctor or nurse under the skin of your upper arm. Once it’s in, you can’t feel it unless you try to find it with your fingers.","Use of hormones, Scalpel included","3","Years","5","Years","Talk with your doctor first and try to avoid having sex or use another contraceptive method until you confirm remedial actions with your doctor.","99","A. If the implant is fitted during the first 5 days of your menstrual cycle you'll be immediately protected against becoming pregnant.
3 | B. If it's fitted on any other day of your menstrual cycle, you'll need to use additional contraceptives (such as condoms) for the first week.","The implant can be removed at any time by a trained doctor or nurse.","A trained doctor or nurses will make a tiny cut in your skin to gently pull the implant out.","The process only takes a few minutes to remove, and a local anesthetic will be used.","Once the implant is removed your ability to get pregnant quickly returns.","It doesn't interrupting sex.
4 | Safe with breastfeeding
5 | Your fertility will return to normal as soon as the implant is taken out.","Headache
6 | Breast tenderness
7 | Acne
8 | Spotting (in the first 6-12 months)
9 | Lighter to no period after a while
10 | Mood swing / Depression","The implant releases the hormone progestogen into your bloodstream, which prevents the release of an egg each month (ovulation) to prevent pregnancy.","Serious problems with Nexplanon are rare, but they include arm pain that lasts for longer than a few days, an infection in the arm that needs medicine, or a scar on your arm where the implant goes.","can't use an Estrogen-based method
11 | have arterial disease or a history of a heart disease or stroke
12 | have liver disease
13 | have breast cancer or have had it in the past
14 | have unexplained bleeding in between periods or after sex","Tell your doctor or nurse if you have any unexpected symptoms while using Nexplanon.","Contraception clinics
15 | Sexual health clinics
16 | GP surgeries","Put in by doctor or nurse.","0","1300","Price may vary from geographic regions and health insurers. But the good news is that implants are totally free (or low cost) with most health insurance plans, Medicaid, and some other government programs.","Needle phobia: Needles will be included in the inserting process. If you don't feel comfortable with that, please inform your doctor in advance.
17 | Is it compatible with your religious beliefs or cultural practices?: Some forms of birth control are considered a violation of certain religious rights or cultural traditions. Weigh the risks and benefits of a birth control method against your personal convictions."
--------------------------------------------------------------------------------
/backend/src/auth/auth.service.ts:
--------------------------------------------------------------------------------
1 | import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
2 | import { JwtService } from '@nestjs/jwt';
3 | import {
4 | AuthenticatedUser,
5 | SignInInfo,
6 | UserAuthPayload,
7 | UserInfo,
8 | } from 'src/types/user';
9 | import { UserService } from 'src/user/user.service';
10 | import { User } from '../entities/user.entity';
11 | import { ConfigService } from '@nestjs/config';
12 |
13 | @Injectable()
14 | export class AuthService {
15 | private readonly maxAge: number;
16 | constructor(
17 | private readonly usersService: UserService,
18 | private jwtService: JwtService,
19 | private configService: ConfigService,
20 | ) {
21 | this.maxAge = this.configService.get('JWT_EXPIRATION');
22 | }
23 |
24 | /**
25 | *
26 | * @param info
27 | */
28 | public async signIn(info: SignInInfo): Promise {
29 | const user = await this.usersService.getUser(info);
30 |
31 | // Create temporary auth token, so we can route them to /login/
32 | const token = await this.createAuthToken({ userId: user.id }, 60);
33 |
34 | return {
35 | id: user.id,
36 | email: user.email,
37 | name: user.name,
38 | accessToken: token,
39 | };
40 | }
41 |
42 | public async signUp(userInfo: UserInfo): Promise {
43 | this.verifyPasswordStrength(userInfo.password);
44 |
45 | const user = await this.usersService.createUser(userInfo);
46 |
47 | // Create temporary auth token, so we can route them to /login/
48 | const token = await this.createAuthToken({ userId: user.id }, 60);
49 |
50 | return {
51 | id: user.id,
52 | email: user.email,
53 | name: user.name,
54 | accessToken: token,
55 | };
56 | }
57 |
58 | /**
59 | * Creates an auth token based on the given payload
60 | * If no expiry time is given, tokens expire after the maxAge property in this service
61 | * Otherwise, tokens expire after the specified number of seconds
62 | * @param payload payload being turned into auth token
63 | * @param expiresIn expiry time in seconds
64 | */
65 | public async createAuthToken(payload: UserAuthPayload, expiresIn?: number) {
66 | const token = await this.jwtService.signAsync(payload, {
67 | expiresIn: expiresIn ? expiresIn : this.maxAge,
68 | });
69 |
70 | if (token === null || token === undefined) {
71 | throw new HttpException('Invalid JWT Token', 500);
72 | }
73 |
74 | return token;
75 | }
76 |
77 | /**
78 | * Returns the payload decoded from the given auth token.
79 | * Does not verify signature (not secure)
80 | *
81 | * @param authToken User Authorization Token
82 | */
83 | public decodeToken(authToken: string): UserAuthPayload {
84 | return this.jwtService.decode(authToken) as UserAuthPayload;
85 | }
86 |
87 | /**
88 | * Returns the payload decoded from the given auth token.
89 | * Verifies the signature (secure)
90 | *
91 | * @param authToken User Authorization Token
92 | */
93 | public async verifyAsync(authToken: string): Promise {
94 | return this.jwtService.verifyAsync(authToken);
95 | }
96 |
97 | /**
98 | * TODO: Implement this
99 | * @param password
100 | */
101 | private verifyPasswordStrength(password: string) {
102 | return undefined;
103 | }
104 |
105 | /**
106 | * Returns the max age of an auth token in seconds.
107 | */
108 | public getTokenMaxAge(): number {
109 | return this.maxAge;
110 | }
111 |
112 | /**
113 | * Gets the user that was specified in the JWT Payload, based on their ID
114 | * @param payload JWT Payload including user ID
115 | */
116 | async validateUser(payload: UserAuthPayload): Promise {
117 | const user = await this.usersService.getById(payload.userId);
118 | if (!user) {
119 | throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
120 | }
121 | return user;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/frontend/components/NavBar.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement, useState } from 'react';
2 | import styled from 'styled-components';
3 | import SvgBookmarkIcon from '../public/desktop-icons/desktop-bookmark.svg';
4 | import SvgSettingsIcon from '../public/desktop-icons/settings.svg';
5 | import SvgSearchIcon from '../public/desktop-icons/search.svg';
6 | import SvgProfileIcon from '../public/desktop-icons/profile.svg';
7 | import { Menu } from 'antd';
8 | import { colors } from '../templates/mediaSizes';
9 | import Link from 'next/link';
10 |
11 | class QuickLink {
12 | title: string;
13 | url: string;
14 |
15 | constructor(title: string, url: string) {
16 | this.title = title;
17 | this.url = url;
18 | }
19 | }
20 |
21 | const quickLinks = [
22 | new QuickLink('Cervical Cap', 'https://www.google.com'),
23 | new QuickLink('Condom', 'https://www.google.com'),
24 | new QuickLink('Copper IUD', 'https://www.google.com'),
25 | new QuickLink('Diaphragm', 'https://www.google.com'),
26 | new QuickLink('Hormonal IUD', 'https://www.google.com'),
27 | new QuickLink('Implant', '/implant'),
28 | new QuickLink('Patch', 'https://www.google.com'),
29 | new QuickLink('Pill', 'https://www.google.com'),
30 | new QuickLink('Ring', 'https://www.google.com'),
31 | new QuickLink('Shot', 'https://www.google.com'),
32 | new QuickLink('Spermicide', 'https://www.google.com'),
33 | new QuickLink('Sterilization', 'https://www.google.com'),
34 | ];
35 |
36 | const DropdownColumns = styled.ul`
37 | position: absolute;
38 | background-color: white;
39 | margin-left: -75px;
40 | padding: 25px;
41 | margin-top: 32px;
42 | columns: 2;
43 | column-gap: 25px;
44 | border-top-style: solid;
45 | border-top-width: 1px;
46 | border-top-color: ${colors.homepageNavBarDropdown};
47 | z-index: 5;
48 | `;
49 |
50 | const StyledMenu = styled(Menu)`
51 | padding: 50px;
52 | `;
53 |
54 | const MenuItem = styled(Menu.Item)`
55 | list-style: none;
56 |
57 | a:hover {
58 | color: ${colors.homepageNavBarDropdown};
59 | text-decoration: underline;
60 | }
61 | `;
62 |
63 | const ArrowDropdown = styled.div`
64 | width: 0;
65 | height: 0;
66 | border-left: 5px solid transparent;
67 | border-right: 5px solid transparent;
68 | border-bottom: 5px solid ${colors.homepageNavBarDropdown};
69 | position: absolute;
70 | margin-top: 27px;
71 | margin-left: 40px;
72 | `;
73 |
74 | const MenuHeading = ({
75 | title,
76 | links = [],
77 | }: {
78 | title: string;
79 | links?: QuickLink[];
80 | }): ReactElement => {
81 | const [dropdown, setDropdown] = useState(false);
82 |
83 | const linkToItem = (link: QuickLink) => {
84 | return (
85 |
90 | );
91 | };
92 |
93 | return (
94 | setDropdown(true)}
96 | onMouseLeave={() => setDropdown(false)}
97 | >
98 | {title}
99 | {dropdown && title == 'Quick Access' ? (
100 |