diff --git a/package.json b/package.json index 10832f3..30f896a 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", - "lint": "vue-cli-service lint" + "lint": "vue-cli-service lint", + "serve:prod": "vue-cli-service serve --mode production" }, "dependencies": { "axios": "^0.19.0", @@ -14,6 +15,7 @@ "serialize-javascript": "^2.1.2", "vue": "^2.6.10", "vue-codemirror": "^4.0.6", + "vue-highlightjs": "^1.3.3", "vue-marquee-text-component": "^1.1.1", "vue-moment": "^4.1.0", "vue-router": "^3.1.3", @@ -37,7 +39,7 @@ "sass": "^1.19.0", "sass-loader": "^8.0.0", "vue-cli-plugin-codemirror": "^0.0.6", - "vue-cli-plugin-vuetify": "^2.0.2", + "vue-cli-plugin-vuetify": "^2.0.3", "vue-template-compiler": "^2.6.10", "vuetify-loader": "^1.3.0" }, diff --git a/public/index.html b/public/index.html index b5e4b1d..de8087a 100644 --- a/public/index.html +++ b/public/index.html @@ -1,45 +1,78 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + CodeWizardsHQ CODE CHALLENGE
- - \ No newline at end of file + diff --git a/src/App.vue b/src/App.vue index e56e576..29b5a32 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,33 +1,9 @@ diff --git a/src/api/auth.js b/src/api/auth.js index 23a5c90..919e7af 100644 --- a/src/api/auth.js +++ b/src/api/auth.js @@ -94,7 +94,12 @@ async function resetPassword(token, password) { ); } +async function doesUsernameExist(username) { + return await request(routes.userapi_user_exists(username)); +} + export default { + doesUsernameExist, logout, login, autoLogin, diff --git a/src/api/routes.js b/src/api/routes.js index cfc35dc..ea9a4bd 100644 --- a/src/api/routes.js +++ b/src/api/routes.js @@ -15,6 +15,9 @@ export default { userapi_refresh: route("/api/v1/users/token/refresh", "POST"), userapi_forgot_password: route("/api/v1/users/forgot", "POST"), userapi_reset_password: route("/api/v1/users/reset-password", "POST"), + userapi_user_exists: username => { + return route(`/api/v1/users/${username}/exists`); + }, questionsapi_rank_reset: route("/api/v1/questions/reset", "DELETE"), questionsapi_answer_next_question: route("/api/v1/questions/answer", "POST"), questionsapi_answer_final_question: route("/api/v1/questions/final", "POST"), diff --git a/src/api/voting.js b/src/api/voting.js index 340f1da..5b07932 100644 --- a/src/api/voting.js +++ b/src/api/voting.js @@ -5,6 +5,16 @@ async function getBallot() { return request(routes.voting_ballot); } +async function cast(answerId, email) { + return request(routes.voting_cast(answerId), { data: { email } }); +} + +async function confirm(token) { + return request(routes.voting_confirm, { data: { token } }); +} + export default { - getBallot + getBallot, + cast, + confirm }; diff --git a/src/components/HelpPopOver.vue b/src/components/HelpPopOver.vue deleted file mode 100644 index 938efc6..0000000 --- a/src/components/HelpPopOver.vue +++ /dev/null @@ -1,61 +0,0 @@ - - - - - diff --git a/src/components/QuizBar.vue b/src/components/QuizBar.vue deleted file mode 100644 index 1a21dab..0000000 --- a/src/components/QuizBar.vue +++ /dev/null @@ -1,68 +0,0 @@ - - - - - diff --git a/src/components/AppBar.vue b/src/components/Toolbars/AppBar.vue similarity index 91% rename from src/components/AppBar.vue rename to src/components/Toolbars/AppBar.vue index 74cbe50..7050206 100644 --- a/src/components/AppBar.vue +++ b/src/components/Toolbars/AppBar.vue @@ -8,7 +8,7 @@ class="pl-4" >
- +
diff --git a/src/components/CWHQBar.vue b/src/components/Toolbars/CWHQBar.vue similarity index 100% rename from src/components/CWHQBar.vue rename to src/components/Toolbars/CWHQBar.vue diff --git a/src/components/LeaderboardBar.vue b/src/components/Toolbars/LeaderboardBar.vue similarity index 98% rename from src/components/LeaderboardBar.vue rename to src/components/Toolbars/LeaderboardBar.vue index b0a04f9..db90463 100644 --- a/src/components/LeaderboardBar.vue +++ b/src/components/Toolbars/LeaderboardBar.vue @@ -1,5 +1,6 @@ + + + + diff --git a/src/main.js b/src/main.js index f8b1747..8c1f136 100644 --- a/src/main.js +++ b/src/main.js @@ -1,12 +1,19 @@ +// don't mess with this order +import "@/styles/fonts.scss"; +import "@/styles/styles.scss"; + import Vue from "vue"; import App from "./App.vue"; -import router from "./plugins/router"; + import vuetify from "./plugins/vuetify"; +import router from "./plugins/router"; import "./plugins/moment"; +import "./plugins/codemirror"; +import "./plugins/highlightjs"; + import store from "./store"; -import "@/styles/styles.scss"; import { auth } from "@/api"; -import "./plugins/codemirror"; + Vue.config.productionTip = false; (async function() { @@ -17,13 +24,15 @@ Vue.config.productionTip = false; try { await auth.autoLogin(); } catch (err) { - // console.error("Was unable to authenticate user"); + // eslint-disable-next-line no-console + console.error("Was unable to authenticate user"); } try { await store.dispatch("Quiz/refresh"); } catch (err) { - // console.error("Was unable to refresh question status", err.reason); + // eslint-disable-next-line no-console + console.error("Was unable to refresh question status", err.reason); } new Vue({ diff --git a/src/plugins/highlightjs.js b/src/plugins/highlightjs.js new file mode 100644 index 0000000..390dded --- /dev/null +++ b/src/plugins/highlightjs.js @@ -0,0 +1,6 @@ +// Import Vue and vue-highlgihtjs +import Vue from "vue"; +import VueHighlightJS from "vue-highlightjs"; + +// Tell Vue.js to use vue-highlightjs +Vue.use(VueHighlightJS); diff --git a/src/plugins/router.js b/src/plugins/router.js index d9ee2bf..a0cbd85 100644 --- a/src/plugins/router.js +++ b/src/plugins/router.js @@ -1,6 +1,6 @@ import Vue from "vue"; import VueRouter from "vue-router"; -import { auth, quiz } from "@/api"; +import { auth } from "@/api"; import store from "@/store"; Vue.use(VueRouter); @@ -17,145 +17,163 @@ function isChallengeClosed() { return store.state.Quiz.quizHasEnded; } +async function logout() { + await store.dispatch("Quiz/reset"); + await auth.logout(); +} + const routes = [ { - path: "/home", - name: "home", + // basically a dynamic home page + path: "/", + name: "redirect", beforeEnter(to, from, next) { if (isChallengeOpen() || isChallengePending()) { - next({ name: 'quiz' }); + next({ name: "quiz" }); return; } if (isChallengeClosed()) { - next({ name: 'voting' }) + next({ name: "voting" }); return; } } }, { - path: "/login", - name: "login", - component: () => import("@/views/Login"), - meta: { - anon: true - } - }, - { - path: "/forgot-password", - name: "forgot-password", - component: () => import("@/views/ForgotPassword"), - meta: { anon: true } - }, - { - path: "/reset-password/:token", - name: "reset-password", - component: () => import("@/views/ResetPassword") - }, - { - path: "/logout", - name: "logout", - async beforeEnter(to, from, next) { - await store.dispatch("Quiz/reset"); - await auth.logout(); - next({ name: "login" }); - }, - meta: { - secured: true - } - }, - { - path: "/create-account", - name: "register", - component: () => import("@/views/Register/index.vue"), - meta: { - anon: true - } - }, - { - path: "/admin", - name: "admin", - component: () => import("@/views/Admin.vue"), - meta: { - secured: true - } + // public routes + path: "/", + component: () => import("@/views/Public/App"), + children: [ + { + path: "frequently-asked-questions", + name: "faq", + component: () => import("@/views/Public/FAQ") + } + ] }, + // FOR EXTERNAL IFRAME { - path: "/voting", - name: "voting", - component: () => import("@/views/Voting/Ballot.vue"), - meta: { - challengeOver: true - } + path: "/iframe/leaderboard", + name: "iframe-leaderboard", + component: () => import("@/views/IFrame/Leaderboard") }, { - // dev only - path: "/leader-board", - name: "leader-board", - component: () => import("@/views/Voting/Leaderboard.vue") + // account routes + path: "/", + component: () => import("@/views/Public/App"), + children: [ + { + path: "login", + name: "login", + component: () => import("@/views/Accounts/Login"), + meta: { anon: true } + }, + { + path: "forgot-password", + name: "forgot-password", + component: () => import("@/views/Accounts/ForgotPassword") + }, + { + path: "reset-password/:token", + name: "reset-password", + component: () => import("@/views/Accounts/ResetPassword") + }, + { + path: "create-account", + name: "register", + component: () => import("@/views/Accounts/Register"), + meta: { anon: true } + }, + { + path: "logout", + name: "logout", + beforeEnter: (to, from, next) => + logout().then(() => next({ name: "login" })), + meta: { auth: true } + }, + { + path: "admin", + name: "admin", + component: () => import("@/views/Accounts/Admin"), + meta: { auth: true } + } + ] }, { - path: "/frequently-asked-questions", - name: "faq", - component: () => import("@/views/FAQ.vue") + // voting routes + path: "/", + component: () => import("@/views/Voting/App"), + meta: { challengeOver: true }, + children: [ + { + path: "voting", + name: "voting", + component: () => import("@/views/Voting/Ballot") + }, + { + path: "vote-confirmation", + name: "voting-confirmation", + component: () => import("@/views/Voting/Confirm") + } + ] }, { - path: "/quiz", - name: "quiz", - component: async () => { - await store.dispatch("Quiz/refresh"); + // quiz routes + path: "/", + component: () => import("@/views/Public/App"), + meta: { secured: true, challengeOpenOrPending: true }, + children: [ + { + path: "quiz", + name: "quiz", + component: async () => { + // CHALLENGE HAS NOT STARTED + if (!isChallengeOpen()) { + return import("@/views/Quiz/QuizCountdown"); + } - // CHALLENGE HAS NOT STARTED - if (!isChallengeOpen()) { - return import("@/views/Quiz/QuizCountdown"); - } - - // USER HAS FINISHED QUIZ - if (store.state.Quiz.maxRank === store.state.User.rank - 1) { - return import("@/views/Quiz/QuizFinished"); - } + // USER HAS FINISHED QUIZ + if (store.state.Quiz.maxRank === store.state.User.rank - 1) { + return import("@/views/Quiz/QuizFinished"); + } - // MUST WAIT FOR NEXT QUESTION - if (store.state.Quiz.awaitNextQuestion) { - return import("@/views/Quiz/QuizCountdown"); - } + // MUST WAIT FOR NEXT QUESTION + if (store.state.Quiz.awaitNextQuestion) { + return import("@/views/Quiz/QuizCountdown"); + } - // SHOW THE LAST QUESTION - if (store.state.Quiz.isLastQuestion) { - return import("@/views/Quiz/QuizFinalQuestion"); - } + // SHOW THE LAST QUESTION + if (store.state.Quiz.isLastQuestion) { + return import("@/views/Quiz/QuizFinalQuestion"); + } - // NORMAL QUIZ MODE - return import("@/views/Quiz/Quiz"); - }, - beforeEnter(from, to, next) { - // USER MUST SEE INTRO VIDEO - if (isChallengeOpen() && !store.state.Quiz.hasSeenIntro && store.state.User.rank == 1) { - next({ name: "quiz-intro" }); - return; + // NORMAL QUIZ MODE + return import("@/views/Quiz/Quiz"); + }, + beforeEnter(from, to, next) { + // USER MUST SEE INTRO VIDEO + if ( + isChallengeOpen() && + !store.state.Quiz.hasSeenIntro && + store.state.User.rank == 1 + ) { + next({ name: "quiz-intro" }); + return; + } + next(); + } + }, + { + path: "/quiz/intro", + name: "quiz-intro", + component: () => import("@/views/Quiz/QuizIntro") } - next(); - }, - meta: { - secured: true, - challengeOpenOrPending: true - } - }, - { - path: "/quiz/intro", - name: "quiz-intro", - component: () => import("@/views/Quiz/QuizIntro"), - meta: { - secured: true, - challengeOpenOrPending: true - } + ] }, { path: "*", - name: "redirect", - redirect: { - name: "home" - } + name: "wildcard", + redirect: { name: "redirect" } } ]; @@ -167,36 +185,57 @@ const router = new VueRouter({ router.beforeEach(async (to, from, next) => { const requireAuth = to.matched.some(record => record.meta.secured); const requireAnon = to.matched.some(record => record.meta.anon); - const requireChallengePending = to.matched.some(record => record.meta.challengePending); - const requireChallengeOpen = to.matched.some(record => record.meta.challengeOpen); - const requireChallengeClosed = to.matched.some(record => record.meta.challengeOver); - const requireChallengeOpenPending = to.matched.some(record => (record.meta.challengeOpenOrPending)); + const requireChallengePending = to.matched.some( + record => record.meta.challengePending + ); + const requireChallengeOpen = to.matched.some( + record => record.meta.challengeOpen + ); + const requireChallengeClosed = to.matched.some( + record => record.meta.challengeOver + ); + const requireChallengeOpenPending = to.matched.some( + record => record.meta.challengeOpenOrPending + ); - if (requireChallengePending || requireChallengeOpen || requireChallengeClosed || requireChallengeOpenPending) { - await store.dispatch("Quiz/refresh"); + if ( + requireChallengePending || + requireChallengeOpen || + requireChallengeClosed || + requireChallengeOpenPending + ) { + try { + await store.dispatch("Quiz/refresh"); - const challengeIsClosed = isChallengeClosed(); - const challengeIsPending = isChallengePending(); - const challengeIsOpen = isChallengeOpen(); + const challengeIsClosed = isChallengeClosed(); + const challengeIsPending = isChallengePending(); + const challengeIsOpen = isChallengeOpen(); - if (!challengeIsClosed && requireChallengeClosed) { - next({ name: 'home' }); - return; - } + if (!challengeIsClosed && requireChallengeClosed) { + next({ name: "redirect" }); + return; + } - if (!challengeIsOpen && requireChallengeOpen) { - next({ name: 'home' }); - return; - } + if (!challengeIsOpen && requireChallengeOpen) { + next({ name: "redirect" }); + return; + } - if (!challengeIsPending && requireChallengePending) { - next({ name: 'home' }); - return; - } + if (!challengeIsPending && requireChallengePending) { + next({ name: "redirect" }); + return; + } - if ((!challengeIsOpen && !challengeIsPending) && requireChallengeOpenPending) { - next({ name: 'home' }); - return; + if ( + !challengeIsOpen && + !challengeIsPending && + requireChallengeOpenPending + ) { + next({ name: "redirect" }); + return; + } + } catch (err) { + //continue } } @@ -209,7 +248,7 @@ router.beforeEach(async (to, from, next) => { } if (isAuthenticated && requireAnon) { - next({ name: "home" }); + next({ name: "redirect" }); return; } } diff --git a/src/plugins/vuetify.js b/src/plugins/vuetify.js index c6178ba..3fd23ba 100644 --- a/src/plugins/vuetify.js +++ b/src/plugins/vuetify.js @@ -1,6 +1,5 @@ import Vue from "vue"; -import Vuetify from "vuetify/lib"; -// import colors from "vuetify/lib/util/colors"; +import Vuetify from "vuetify/lib/framework"; Vue.use(Vuetify); @@ -10,17 +9,19 @@ export default new Vuetify({ flat: true, themes: { dark: { + accent: "#82B1FF", + error: "#FF5252", + info: "#2196F3", + success: "#4CAF50", + warning: "#FFC107", primary: "#fdc743", secondary: "#27AE82", - // accent: '#82B1FF', - // error: '#FF5252', - // info: '#2196F3', - // success: '#4CAF50', - // warning: '#FFC107', dark: "#282828", dark2: "#1E1E1E", button: "#4CAF50", - input: "#4CAF50" + input: "#4CAF50", + cwhqBlue: "#0d1d41", + cwhqYellow: "#fdc743" } } } diff --git a/src/styles/application.scss b/src/styles/application.scss index 9bd1365..c75e914 100644 --- a/src/styles/application.scss +++ b/src/styles/application.scss @@ -1,5 +1,15 @@ -.theme--dark.v-application { +&.theme--dark.v-application { // background: url("/images/background.jpg"); // background-size: cover; - background-color: #21252D; + background-color: #21252d; + + &.voting { + background-color: #f8f8f9; + font-family: "Barlow", sans-serif; + .v-btn { + font-weight: bold; + font-size: 14px; + font-family: "Barlow", sans-serif; + } + } } diff --git a/src/styles/ballot-card.scss b/src/styles/ballot-card.scss new file mode 100644 index 0000000..b99ed27 --- /dev/null +++ b/src/styles/ballot-card.scss @@ -0,0 +1,58 @@ +$ballot-blue: $cwhqBlue; +$ballot-yellow: $cwhqYellow; +$ballot-gray: #ddd; + +.ballot { + .circle { + width: 100px; + height: 100px; + background-color: $ballot-blue; + color: $ballot-yellow; + border-radius: 50% !important; + display: inline-block; + font-size: 50px; + line-height: 90px; + text-align: center; + font-family: "Barlow", sans-serif; + font-weight: 700; + margin-bottom: 20px; + } +} + +.ballot-card { + margin: 25px; + width: 200px; + height: 300px; + background-color: #fff !important; + + border: solid 2px $ballot-gray !important; + border-radius: 0 !important; + text-align: center; + font-family: "Barlow", sans-serif; + color: $ballot-blue; + padding: 20px; + cursor: pointer; + + &:hover { + border-color: $ballot-yellow !important; + } + + .v-btn { + margin-bottom: 10px; + } + + .vote-count { + padding: 0; + text-transform: uppercase; + line-height: 12px; + margin-bottom: 20px; + &:after { + content: ""; + display: inline-block; + position: relative; + width: 100%; + height: 1px; + background-color: $ballot-gray; + } + } +} diff --git a/src/styles/ballot-modal.scss b/src/styles/ballot-modal.scss new file mode 100644 index 0000000..65baa4b --- /dev/null +++ b/src/styles/ballot-modal.scss @@ -0,0 +1,88 @@ +.v-card.ballot-modal { + padding: 0; + height: 600px; + + pre, + code { + width: 100% !important; + height: 100%; + background-color: $cwhqBlue; + color: $cwhqYellow; + display: block; + padding: 0; + margin: 0; + text-align: left; + } + + code { + padding: 20px; + } + + .main-row { + height: 600px; + + > * { + height: 600px; + } + } + + .left { + padding: 20px; + text-align: center; + + * { + box-shadow: none !important; + text-align: center !important; + } + + .v-label { + width: 100%; + max-width: 100%; + text-align: center !important; + font-weight: bold !important; + font-family: "Barlow", sans-serif; + } + } + .v-text-field--outlined fieldset { + border-color: $cwhqYellow !important; + border: solid 2px $cwhqBlue; + } + + .v-input { + border-radius: 0; + font-weight: bold; + color: #8c919c; + text-align: center !important; + height: 67px; + } + + .v-text-field__slot { + box-shadow: none !important; + background: none; + text-align: center; + } + + .v-btn { + height: 50px; + font-size: 16px !important; + } + + hr { + margin-bottom: 20px; + color: #ddd; + height: 1px; + opacity: 0.4; + } + + .icons { + width: 100%; + height: 40px; + margin-top: 12px; + + .v-btn { + margin: 0 !important; + padding: 0 !important; + font-size: 20px !important; + } + } +} diff --git a/src/styles/buttons.scss b/src/styles/buttons.scss new file mode 100644 index 0000000..822095c --- /dev/null +++ b/src/styles/buttons.scss @@ -0,0 +1,3 @@ +.v-btn.cwhqBlue { + color: $cwhqYellow; +} \ No newline at end of file diff --git a/src/styles/colors.scss b/src/styles/colors.scss index 8cde5b9..34fff0c 100644 --- a/src/styles/colors.scss +++ b/src/styles/colors.scss @@ -9,3 +9,5 @@ $dark: #282828; $dark2: #1e1e1e; $button: #4caf50; $input: #4caf50; +$cwhqBlue: #0d1d41; +$cwhqYellow: #fdc743; \ No newline at end of file diff --git a/src/styles/quiz-answer.scss b/src/styles/quiz-answer.scss index 9ab618e..3f4b4c7 100644 --- a/src/styles/quiz-answer.scss +++ b/src/styles/quiz-answer.scss @@ -16,11 +16,11 @@ .v-btn { width: 100%; - color: $dark; + color: $dark !important; font-family: "Barrow2Bold"; font-size: 40px; letter-spacing: 12px; - height: 80px; + height: 80px !important; margin-top: 12px; } diff --git a/src/styles/styles.scss b/src/styles/styles.scss index 40bed0e..c5d9c4c 100644 --- a/src/styles/styles.scss +++ b/src/styles/styles.scss @@ -1,32 +1,36 @@ -@import "~vuetify/src/styles/styles.sass"; -@import "./colors.scss"; -@import "./application.scss"; -@import "./quiz-scroll.scss"; -@import "./quiz-answer.scss"; -@import "./quiz-need-help.scss"; -@import "./quiz-bar-rank.scss"; -@import "./content.scss"; -@import "./social-pop-over.scss"; -@import "./casing.scss"; -@import "./cwhq-bar.scss"; -@import "./quiz-bar.scss"; -@import "./leaderboard-bar.scss"; +.cwhq-app { + @import "./colors.scss"; + @import "./application.scss"; + @import "./quiz-scroll.scss"; + @import "./quiz-answer.scss"; + @import "./quiz-need-help.scss"; + @import "./quiz-bar-rank.scss"; + @import "./content.scss"; + @import "./social-pop-over.scss"; + @import "./casing.scss"; + @import "./cwhq-bar.scss"; + @import "./quiz-bar.scss"; + @import "./leaderboard-bar.scss"; + @import "./ballot-card.scss"; + @import "./ballot-modal.scss"; + @import "./buttons.scss"; -.v-alert { - font-size: 20px; -} + .v-alert { + font-size: 20px; + } -.v-input { - font-size: 20px; -} + .v-input { + font-size: 20px; + } -.v-messages { - font-size: 16px; -} + .v-messages { + font-size: 16px; + } -.v-snack { - .v-snack__content { - font-size: 20px; - padding: 20px; + .v-snack { + .v-snack__content { + font-size: 20px; + padding: 20px; + } } } diff --git a/src/styles/v-card.scss b/src/styles/v-card.scss deleted file mode 100644 index a609b02..0000000 --- a/src/styles/v-card.scss +++ /dev/null @@ -1,3 +0,0 @@ -// .v-card__text { -// font-size: 1rem; -// } \ No newline at end of file diff --git a/src/styles/variables.scss b/src/styles/variables.scss index 38dc234..c75eabd 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -1,9 +1,7 @@ // https://vuetifyjs.com/en/customization/sass-variables -@import "./fonts.scss"; // @import "./colors.scss"; $body-font-family: "Barrow2Light"; $heading-font-family: "buWicked"; -@import "v-card.scss"; // $border-radius-root: 6px; $font-size-root: 20px; diff --git a/src/views/Admin.vue b/src/views/Accounts/Admin.vue similarity index 100% rename from src/views/Admin.vue rename to src/views/Accounts/Admin.vue diff --git a/src/views/ForgotPassword.vue b/src/views/Accounts/ForgotPassword.vue similarity index 100% rename from src/views/ForgotPassword.vue rename to src/views/Accounts/ForgotPassword.vue diff --git a/src/views/Login.vue b/src/views/Accounts/Login.vue similarity index 100% rename from src/views/Login.vue rename to src/views/Accounts/Login.vue diff --git a/src/views/Register/DateOfBirthField.vue b/src/views/Accounts/Register/DateOfBirthField.vue similarity index 100% rename from src/views/Register/DateOfBirthField.vue rename to src/views/Accounts/Register/DateOfBirthField.vue diff --git a/src/views/Accounts/Register/Step1.vue b/src/views/Accounts/Register/Step1.vue new file mode 100644 index 0000000..4714151 --- /dev/null +++ b/src/views/Accounts/Register/Step1.vue @@ -0,0 +1,122 @@ + + + diff --git a/src/views/Register/Step2.vue b/src/views/Accounts/Register/Step2.vue similarity index 99% rename from src/views/Register/Step2.vue rename to src/views/Accounts/Register/Step2.vue index aee26b9..61023fb 100644 --- a/src/views/Register/Step2.vue +++ b/src/views/Accounts/Register/Step2.vue @@ -4,6 +4,7 @@ + + + + + + + + + + + + + + + + You are not 13 years of age. +

+ In order to continue with the code challenge you must have your + parent's permission. Have your parent or guardian complete the rest of + this page. +

+ +
+
+ + + Back + + + Next + + + +
+ + + diff --git a/src/views/Register/Step3.vue b/src/views/Accounts/Register/Step4.vue similarity index 98% rename from src/views/Register/Step3.vue rename to src/views/Accounts/Register/Step4.vue index cac10da..f4e926f 100644 --- a/src/views/Register/Step3.vue +++ b/src/views/Accounts/Register/Step4.vue @@ -57,7 +57,7 @@ import TermsOfServiceContent from "@/components/TermsOfServiceContent"; export default { - name: "register-step-2", + name: "register-step-4", props: ["fields"], components: { TermsOfServiceContent }, methods: { diff --git a/src/views/Register/index.vue b/src/views/Accounts/Register/index.vue similarity index 63% rename from src/views/Register/index.vue rename to src/views/Accounts/Register/index.vue index 753404a..1260677 100644 --- a/src/views/Register/index.vue +++ b/src/views/Accounts/Register/index.vue @@ -27,22 +27,35 @@ - Parent Details + + + + Terms Of Use - + - + - + + + + + @@ -57,13 +70,16 @@ import PageCard from "@/components/PageCard"; import Step1 from "./Step1"; import Step2 from "./Step2"; import Step3 from "./Step3"; -import { auth } from "@/api"; +import Step4 from "./Step4"; + +import * as api from "@/api"; export default { components: { Step1, Step2, Step3, + Step4, PageCard }, methods: { @@ -73,23 +89,25 @@ export default { this.stepperIndex = 1; } }, - submit1(cb) { - this.stepperIndex++; - cb(); - }, - submit2(cb) { + next(cb) { this.stepperIndex++; cb(); }, - async submit3(cb) { + async submit(cb) { if (this.isSubmitting) { return; } this.isSubmitting = true; try { - await auth.createAccount({ + await api.auth.createAccount({ + foundUs: + this.fields.heardAboutUs === "Other" + ? this.fields.heardAboutUsText.value + : this.fields.heardAboutUs.value, studentFirstName: this.fields.firstName.value, studentLastName: this.fields.lastName.value, + parentFirstName: this.fields.parentFirstName.value, + parentLastName: this.fields.parentLastName.value, username: this.fields.username.value, parentEmail: this.fields.parentEmail.value, studentEmail: this.fields.studentEmail.value, @@ -108,7 +126,6 @@ export default { cb(); } }, - data() { return { isSubmitting: false, @@ -119,7 +136,15 @@ export default { hint: "You will use this to log in", type: "text", value: "", - rules: [v => !!v || "Please provide a email"] + rules: [ + v => !!v || "Please provide a username", + () => + !this.fields.username.inUse || "This username is already in use" + ], + inUse: false, + requestCount: 0, + requestIndex: 0, + errorMessages: [] }, parentEmail: { label: "Parent's E-mail Address", @@ -139,7 +164,9 @@ export default { value: "", rules: [ v => !!v || "Don't forget to give a password", - v => v.length >= 8 || "Password must be at least 8 characters" + v => v.length >= 8 || "Password must be at least 8 characters", + v => + v.length < 100 || "Password must be at less than 100 characters" ] }, passwordConfirm: { @@ -162,6 +189,44 @@ export default { value: "", rules: [v => !!v || "Please tell us your name"] }, + parentFirstName: { + label: "Parent's First Name", + type: "text", + value: "", + rules: [v => !!v || "Please tell us your name"] + }, + parentLastName: { + label: "Parent's Last Name", + type: "text", + value: "", + rules: [v => !!v || "Please tell us your name"] + }, + heardAboutUs: { + label: "How did you hear about the Code Challange?", + type: "select", + items: [ + "Choose an option", + "I'm a CodeWizardsHQ Student", + "CWHQ newsletter", + "CWHQ website", + "Facebook, Twitter, Instagram, or LinkedIn", + "Friend or family member ", + "Google or search engine", + "Your school or PTA", + "Other" + ], + value: "Choose an option", + rules: [v => v !== "Choose an option" || "Please choose an option"] + }, + heardAboutUsText: { + label: "Tell us where you heard about the Code Challenge!", + type: "text", + value: "", + rules: [ + v => + !!v || "Please tell us where you heard about the code challenge" + ] + }, dateOfBirth: { label: "Student's Date Of Birth", type: "date", diff --git a/src/views/ResetPassword.vue b/src/views/Accounts/ResetPassword.vue similarity index 100% rename from src/views/ResetPassword.vue rename to src/views/Accounts/ResetPassword.vue diff --git a/src/views/FAQ.vue b/src/views/FAQ.vue deleted file mode 100644 index 098338c..0000000 --- a/src/views/FAQ.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - diff --git a/src/views/IFrame/Leaderboard.vue b/src/views/IFrame/Leaderboard.vue new file mode 100644 index 0000000..7a1b3ed --- /dev/null +++ b/src/views/IFrame/Leaderboard.vue @@ -0,0 +1,15 @@ + + + diff --git a/src/views/Public/App.vue b/src/views/Public/App.vue new file mode 100644 index 0000000..b7aef77 --- /dev/null +++ b/src/views/Public/App.vue @@ -0,0 +1,28 @@ + + + diff --git a/src/views/Public/FAQ/FAQCard.vue b/src/views/Public/FAQ/FAQCard.vue new file mode 100644 index 0000000..6de38e1 --- /dev/null +++ b/src/views/Public/FAQ/FAQCard.vue @@ -0,0 +1,12 @@ + + + diff --git a/src/views/Public/FAQ/index.vue b/src/views/Public/FAQ/index.vue new file mode 100644 index 0000000..ac2156f --- /dev/null +++ b/src/views/Public/FAQ/index.vue @@ -0,0 +1,118 @@ + + + diff --git a/src/views/Register/Step1.vue b/src/views/Register/Step1.vue deleted file mode 100644 index 19ccf63..0000000 --- a/src/views/Register/Step1.vue +++ /dev/null @@ -1,77 +0,0 @@ - - - diff --git a/src/views/Voting/App.vue b/src/views/Voting/App.vue new file mode 100644 index 0000000..0ed1991 --- /dev/null +++ b/src/views/Voting/App.vue @@ -0,0 +1,22 @@ + + + diff --git a/src/views/Voting/Ballot.vue b/src/views/Voting/Ballot.vue index f535ad8..f016711 100644 --- a/src/views/Voting/Ballot.vue +++ b/src/views/Voting/Ballot.vue @@ -1,17 +1,92 @@ + + diff --git a/src/views/Voting/BallotCard.vue b/src/views/Voting/BallotCard.vue new file mode 100644 index 0000000..1b807cf --- /dev/null +++ b/src/views/Voting/BallotCard.vue @@ -0,0 +1,33 @@ + + + diff --git a/src/views/Voting/CodeModal.vue b/src/views/Voting/CodeModal.vue new file mode 100644 index 0000000..70b3092 --- /dev/null +++ b/src/views/Voting/CodeModal.vue @@ -0,0 +1,157 @@ + + + diff --git a/src/views/Voting/Confirm.vue b/src/views/Voting/Confirm.vue new file mode 100644 index 0000000..5bf973f --- /dev/null +++ b/src/views/Voting/Confirm.vue @@ -0,0 +1,54 @@ + + + diff --git a/src/views/Voting/Leaderboard.vue b/src/views/Voting/Leaderboard.vue deleted file mode 100644 index 92b628b..0000000 --- a/src/views/Voting/Leaderboard.vue +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/vue.config.js b/vue.config.js index d5f35be..c6295b9 100644 --- a/vue.config.js +++ b/vue.config.js @@ -2,18 +2,13 @@ module.exports = { lintOnSave: false, transpileDependencies: ["vuetify"], outputDir: "dist", - css: { - extract: { ignoreOrder: true }, - }, devServer: { disableHostCheck: true, proxy: { "/api/*": { - // Forward frontend dev server request for /api to flask dev server target: "http://localhost:5000/" }, "/assets/*": { - // Forward frontend dev server request for /api to flask dev server target: "http://localhost:5000/" } } diff --git a/yarn.lock b/yarn.lock index e7edc44..8ff171f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4085,6 +4085,11 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +highlight.js@*: + version "9.18.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.0.tgz#6b1763cfcd53744313bd3f31f1210f7beb962c79" + integrity sha512-A97kI1KAUzKoAiEoaGcf2O9YPS8nbDTCRFokaaeBhnqjQTvbAuAJrQMm21zw8s8xzaMtCQBtgbyGXLGxdxQyqQ== + highlight.js@^9.6.0: version "9.16.2" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.16.2.tgz#68368d039ffe1c6211bcc07e483daf95de3e403e" @@ -7147,7 +7152,14 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1: +resolve@^1.1.6: + version "1.15.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.0.tgz#1b7ca96073ebb52e741ffd799f6b39ea462c67f5" + integrity sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw== + dependencies: + path-parse "^1.0.6" + +resolve@^1.10.0, resolve@^1.12.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1: version "1.12.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== @@ -8453,12 +8465,11 @@ vue-cli-plugin-codemirror@^0.0.6: resolved "https://registry.yarnpkg.com/vue-cli-plugin-codemirror/-/vue-cli-plugin-codemirror-0.0.6.tgz#ccc884bfe4b96eb453379d604fe10ba902b9b4fa" integrity sha512-F6eRARgT2XoJlcOkRRn5TS3W/i/vyQ0oXNvQiOhd7DXclfgCgHv4V9PpoIYYASbAjdJlqpsUMc4k1Dau6QOxgg== -vue-cli-plugin-vuetify@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.0.2.tgz#160e573d59f2594b89531e95b0fdd74ca76ecd9d" - integrity sha512-OJ1YUSfDlQibj111QMGv4a44atmtWdrkynk4voiEuivUqLcZGCJyF/A8ae0VojpMU2jlvZydz0nXjmZmcg+Nqw== +vue-cli-plugin-vuetify@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.0.3.tgz#a4bd1e7b92abadbd9cb64629fef8b139c9d3337b" + integrity sha512-bBQYE5lT5+gbR3Hvyy3/wMLHm+2yxKz+czgz3YK6T5Ykfva4ecK6OQZs0/XebT+YOPoy60qVEk9HYUvG8UFsTQ== dependencies: - semver "^6.0.0" shelljs "^0.8.3" vue-codemirror@^4.0.6: @@ -8481,6 +8492,13 @@ vue-eslint-parser@^5.0.0: esquery "^1.0.1" lodash "^4.17.11" +vue-highlightjs@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/vue-highlightjs/-/vue-highlightjs-1.3.3.tgz#29a0d57132fc1ce15cfa61e896918f5b718c5d52" + integrity sha1-KaDVcTL8HOFc+mHolpGPW3GMXVI= + dependencies: + highlight.js "*" + vue-hot-reload-api@^2.3.0: version "2.3.4" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2" @@ -8553,16 +8571,16 @@ vue@^2.6.10: integrity sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ== vuetify-loader@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/vuetify-loader/-/vuetify-loader-1.3.1.tgz#e2462e485d1f2b1a9a7b2e20b3dc69f1396f23ca" - integrity sha512-ZUGo248CdREWNNk/eETU7or64Xmgily0Dfw61Hj0BDG9fpN0E0bzC7dWwC3BXj3psZjFxLl7ff7KV8qxR4maoA== + version "1.4.3" + resolved "https://registry.yarnpkg.com/vuetify-loader/-/vuetify-loader-1.4.3.tgz#df1323c558be09890877e5fbe817b3a71a6c538d" + integrity sha512-fS0wRil682Ebsj2as+eruBoMPKaQYDhu/fDAndnTItzSY4RK4LOEIsssVL4vD6QY8dvUgoGL84SUQ6vGr777CA== dependencies: loader-utils "^1.2.0" vuetify@^2.1.0: - version "2.1.9" - resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-2.1.9.tgz#2c91163090675cde2cd1b6208829ac434d7d8877" - integrity sha512-52CgEyPoGYHba5yocYKBB/LXcikoWzj9jCDTH8LlzH/hvjzkgsuEtFwUustGHyV9GstRaNZOrk4nuUWbPZc3kQ== + version "2.2.8" + resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-2.2.8.tgz#9e9105503d38e66c0fad27aba08de5e65ff6ba99" + integrity sha512-qeN+X/6L1Xi3Mmog9PMDUKYjC2ZOk3trl7/vDMuP0gcH1QReKb9tqpLjAyP8niOgnvZcUQw4jSc7e4Esju8Seg== vuex@^3.0.1: version "3.1.2"