diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..9116baf --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or + advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email + address, without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +m@eamonma.com, verchshon@gmail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/components/ShareLink.tsx b/components/ShareLink.tsx index f75050b..a8e8966 100644 --- a/components/ShareLink.tsx +++ b/components/ShareLink.tsx @@ -46,7 +46,7 @@ const ShareLink = () => { const { data: shortLink, isLoading, isError } = useShortLink(fullUrl) useEffect(() => { - if(shortLink?.shortUrl) setUrlToShare(shortLink.shortUrl) + if (shortLink?.shortUrl) setUrlToShare(shortLink.shortUrl) }, [shortLink]) const { onCopy, hasCopied } = useClipboard(urlToShare) diff --git a/components/TimetableCreationButton.tsx b/components/TimetableCreationButton.tsx new file mode 100644 index 0000000..3a3a789 --- /dev/null +++ b/components/TimetableCreationButton.tsx @@ -0,0 +1,58 @@ +import React from "react" + +import { Box, Button, useDisclosure } from "@chakra-ui/react" +import { useTranslation } from "next-i18next" +import TimetableCreationModal from "./TimetableCreationModal" + +type Props = { + // newLoading: boolean + // setNewLoading: React.Dispatch> + timetables: { [id: string]: { key: string; name: string } } +} + +const TimetableCreationButton = ({ + // newLoading, + // setNewLoading, + timetables, +}: Props) => { + const { isOpen, onOpen, onClose } = useDisclosure() + + const { t } = useTranslation("index") + + return ( + <> + + + + ) +} + +export default TimetableCreationButton diff --git a/components/TimetableCreationModal.tsx b/components/TimetableCreationModal.tsx new file mode 100644 index 0000000..31b96d2 --- /dev/null +++ b/components/TimetableCreationModal.tsx @@ -0,0 +1,140 @@ +import React, { useState } from "react" +import { + Text, + Flex, + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalFooter, + ModalBody, + ModalCloseButton, + Button, + VStack, + FormControl, + FormHelperText, + Select, + Input, +} from "@chakra-ui/react" +import { useTranslation } from "next-i18next" +import { useMutation } from "@apollo/client" +import { useRouter } from "next/router" +import { CREATE_TIMETABLE } from "../operations/mutations/createTimetable" + +type Props = { + isOpen: boolean + onClose: () => void + timetables: { [id: string]: { key: string; name: string } } +} + +const TimetableCreationModal = ({ isOpen, onClose, timetables }: Props) => { + const { t } = useTranslation("index") + const [newLoading, setNewLoading] = useState(false) + const [name, setName] = useState("") + + const [createTimetable] = useMutation(CREATE_TIMETABLE) + const router = useRouter() + + const createNewTimetable = () => { + setNewLoading(true) + + createTimetable({ + onCompleted: (data) => { + const { + key, + timetable: { id, name }, + } = data.createTimetable + localStorage.setItem( + "timetables", + JSON.stringify({ + ...timetables, + [id]: { key, name }, + }) + ) + + router.push(`/timetable/${id}`) + }, + variables: { + name: name || undefined, + }, + }) + } + + return ( + + + +
{ + e.preventDefault() + createNewTimetable() + }} + > + Create a new timetable + {!newLoading && } + + + + + + {t("select-term")} + + + + + {t("soon-support-summer-2023")} + + + + + + + {t("name")} + + setName(e.target.value)} + autoFocus + width="auto" + /> + + + {t("auto-generated-name")} + + + + + + + + + + +
+
+ ) +} + +export default TimetableCreationModal diff --git a/operations/mutations/createTimetable.tsx b/operations/mutations/createTimetable.tsx index cf7c0d4..f603970 100644 --- a/operations/mutations/createTimetable.tsx +++ b/operations/mutations/createTimetable.tsx @@ -1,8 +1,8 @@ import { gql } from "@apollo/client" export const CREATE_TIMETABLE = gql` - mutation { - createTimetable { + mutation ($name: String) { + createTimetable(name: $name) { timetable { id name diff --git a/pages/index.tsx b/pages/index.tsx index d21c6bc..74ff5e8 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -3,7 +3,6 @@ import { useDebouncedCallback } from "use-debounce" import { SearchIcon } from "@chakra-ui/icons" import { Box, - Button, CloseButton, Container, Flex, @@ -18,6 +17,8 @@ import { Tab, TabList, Tabs, + Tag, + TagLabel, Tooltip, useColorMode, useColorModeValue, @@ -40,6 +41,7 @@ import Layout from "../components/Layout" import { FaMoon, FaSun } from "react-icons/fa" import { IconType } from "react-icons" import { theme } from "./_app" +import TimetableCreationButton from "../components/TimetableCreationButton" /** * Darkens a color by a percentage @@ -145,28 +147,6 @@ const Dashboard = () => { debounced(searchQuery) }, [timetables, setTimetablesToDisplay, searchQuery]) - const createNewTimetable = () => { - setNewLoading(true) - - createTimetable({ - onCompleted: (data) => { - const { - key, - timetable: { id, name }, - } = data.createTimetable - localStorage.setItem( - "timetables", - JSON.stringify({ - ...timetables, - [id]: { key, name }, - }) - ) - - router.push(`/timetable/${id}`) - }, - }) - } - const [pageLoading, setPageLoading] = useState("") const [longLoadTime, setLongLoadTime] = useState(false) @@ -194,7 +174,7 @@ const Dashboard = () => { // Differences in the UI between light and dark mode // Mostly for icons/hover - interface ModeConfig { + type ModeConfig = { tLabel: string icon: IconType iconId: string @@ -349,6 +329,29 @@ const Dashboard = () => { setPageLoading(id) }} > + + + + 2022 {t("fall")}–2023 {t("winter")} + + + + { ) })} - + + - { - if (index === 2 || index === 3) return - setTabIndex(index) + - - {t("my-timetables")} - - {t("shared-with")} - - {t("common:about")} - - - - - - {" "} - {t(modeConfig.tLabel)} - - - - - + { + if (index === 2 || index === 3) return + setTabIndex(index) + }} + > + + {t("my-timetables")} + + {t("shared-with")} + + {t("common:about")} + + + + + + {" "} + {t(modeConfig.tLabel)} + + + + + + diff --git a/public/locales/en/index.json b/public/locales/en/index.json index cfc5423..2006c08 100644 --- a/public/locales/en/index.json +++ b/public/locales/en/index.json @@ -1,11 +1,17 @@ { "by-creation-date": "Creation date", - "create-timetable": "Create a new timetable", + "create-timetable": "Create timetable", "my-timetables": "My timetables", "shared-with": "Shared with me", "templates": "Templates", "settings": "Settings", "dark-mode": "Dark mode", "light-mode": "Light mode", - "search": "Search" + "search": "Search", + "fall": "Fall", + "winter": "Winter", + "soon-support-summer-2023": "Sqrl will soon support creating timetables for the summer of 2023.", + "select-term": "Select term", + "name": "Name timetable", + "auto-generated-name": "Leave the name blank to have it auto-generated" } diff --git a/src/utils/fetcher.ts b/src/utils/fetcher.ts index d5ada0b..6366619 100644 --- a/src/utils/fetcher.ts +++ b/src/utils/fetcher.ts @@ -1,3 +1,2 @@ // @ts-ignore -export default (...args) => fetch(...args).then(res => res.json()) - +export default (...args) => fetch(...args).then((res) => res.json()) diff --git a/src/utils/getSharePrefix.ts b/src/utils/getSharePrefix.ts index bd222e8..8100566 100644 --- a/src/utils/getSharePrefix.ts +++ b/src/utils/getSharePrefix.ts @@ -1 +1,2 @@ -export default () => `${window.location.protocol}//${window.location.host}/timetable/` +export default () => + `${window.location.protocol}//${window.location.host}/timetable/`