| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- //static/js/frontend.js
- function setLoggedIn(loggedInBoolean) {
- let body = document.body;
- body.dataset.loggedIn = !!loggedInBoolean;
- }
- function createExistingVote(voter, vote) {
- let voteDiv = document.createElement('div');
- voteDiv.innerHTML = "";
- if (vote === "yes") {
- voteDiv.classList.add('vote-yes');
- } else if (vote === "no") {
- voteDiv.classList.add('vote-no');
- }
- voteDiv.innerText = voter;
- return voteDiv;
- }
- function createOneDayCard(dayData, currentSession, weather) {
- let date = new Date(dayData.date + 'T00:00:00');
- let card = document.createElement('div');
- let temp = weather.main.temp;
- let icon = weather.weather[0].icon;
- card.innerHTML = `
- <div class="cardtop">
- <div class="date">
- <div class="dow">${date.toLocaleString("en-CA", { weekday: 'long' })}</div>
- <div class="dom">${date.toLocaleString("en-CA", { month: 'short', day: 'numeric' })}</div>
- </div>
- <div class="weather">
- <div class="temp">
- ${temp}°C
- </div>
- <div class="weath">
- <img src="https://openweathermap.org/img/wn/${icon}@2x.png" alt="${
- weather.weather[0].description
- }">
- </div>
- </div>
- </div>
- <div class="make-vote">
- <div class="satis-tier forloggedin">
- Can you attend?
- <div class="">
- <button class="vote yes" data-vote="yes">
- Yes ✔️
- </button>
- <button class="vote maybe" data-vote="">
- ??
- </button>
- <button class="vote no" data-vote="no">
- No ❌
- </button>
- </div>
- </div>
- </div>
- <div class="existing-votes">
- </div>
- `
- card.classList.add("card")
- card.dataset.date = dayData.date;
- if (weather) {
- // you need to figure this part out yourself
- }
- let existingVotesDiv = card.querySelector('.existing-votes');
- for (let [voter, vote] of Object.entries(dayData.votes)) {
- existingVotesDiv.append(createExistingVote(voter, vote))
- }
- return card;
- }
- function updateVotableDays(daysWithVotes, currentSession, weatherForecasts) {
- let daysView = document.querySelector(".days-view");
- if (!daysView) {
- console.error("could not find element to put days into")
- return;
- }
- daysView.innerHTML = '';
- for (let date in daysWithVotes) {
- let votes = daysWithVotes[date];
- let weather = weatherForecasts.list.find((w) => w.dt_txt.startsWith(date));
- daysView.append(createOneDayCard({
- date,
- votes
- }, currentSession, weather))
- }
- }
- class FrontendState {
- constructor() {
- this.currentSession = undefined;
- this.daysWithVotes = [];
- this.weatherForecasts = {};
- }
- async refreshAllState(updateView = true) {
- await Promise.all([
- this.refreshVotesState(false),
- this.refreshWeatherState(false),
- this.refreshSessionState(false),
- ])
- if (updateView) {
- this.updateView();
- }
- }
- async refreshVotesState(updateView = true) {
- let {
- success,
- data,
- error
- } = await getVotesFromBackend()
- if (success) {
- this.daysWithVotes = data;
- } else {
- // ha ha I'm being lazy. can you do better?
- updateErrorMessage(error)
- }
- if (updateView) {
- this.updateView();
- }
- }
- async refreshWeatherState(updateView = true) {
- let {
- success,
- data,
- error
- } = await fetchWeatherForCurrentLocation()
- if (success) {
- this.weatherForecasts = data;
- } else {
- // ha ha I'm being lazy. can you do better?
- updateErrorMessage(error)
- }
- if (updateView) {
- this.updateView();
- }
- }
- async refreshSessionState(updateView = true) {
- let {
- success,
- data,
- error
- } = await getSessionFromBackend()
- if (success) {
- this.currentSession = data;
- } else {
- // 處理錯誤情況
- updateErrorMessage('Unable to refresh session state. Please try again later.');
- }
- if (updateView) {
- this.updateView();
- }
- }
- async updateView() {
- // 1. update the whole frontend so that it shows relevant logged-in vs logged-out features
- setLoggedIn(!!this.currentSession)
- // 2. optionally update the header so that it shows the username of the person logged in
- if(this.currentSession)
- updateHeader(this.currentSession.username)
- else
- updateHeader('')
- // 3. render the days
- updateVotableDays(this.daysWithVotes, this.currentSession, this.weatherForecasts)
- }
- }
- const fes = new FrontendState();
- fes.refreshAllState()
- async function handleAuthEvent(event) {
- event.preventDefault();
- // console.log(event.currentTarget, event.target)
- let usernameInput = event.currentTarget.querySelector('#headerusername');
- let usernameValue = usernameInput.value;
- let passwordInput = event.currentTarget.querySelector('#headerpassword');
- let passwordValue = passwordInput.value;
- let button = event.target.closest('button');
- if (button) {
- let authActionName = button?.dataset?.authAction;
- let authActionFunction = {
- signup: ajaxSignup,
- login: ajaxLogin,
- logout: ajaxLogout,
- } [authActionName];
- if (authActionFunction) {
- let authResult = await authActionFunction(usernameValue, passwordValue);
- if (authResult && authResult.success) {
- await fes.refreshSessionState();
- usernameInput.value = passwordInput.value = '';
- } else if (authResult) {
- // 處理登入、註冊或登出失敗的情況
- updateErrorMessage(authResult.error);
- } else {
- // 處理未知網絡錯誤
- updateErrorMessage("unknown network error");
- }
- }
- }
- }
- function updateHeader(username) {
- const usernameSpan = document.querySelector('.username');
- usernameSpan.textContent = username ? username : 'nobody';
- }
- function updateErrorMessage(message) {
- const errorMessageDiv = document.querySelector('.error-message');
- errorMessageDiv.textContent = message;
- }
- const authform = document.querySelector('form.authform')
- authform.addEventListener("click", handleAuthEvent);
- async function handleVoteEvent(event) {
- event.preventDefault();
- let button = event.target.closest('button.vote');
- if (button) {
- let voteVal = button.dataset.vote; // 從按鈕的 data-vote 屬性中獲取投票選項
- let cardDiv = button.closest("div.card");
- let date = cardDiv.dataset.date; // 從卡片的 data-date 屬性中獲取日期
- if (fes.currentSession && fes.currentSession.username && voteVal && date) {
- // 假設用戶已登入,並且 fes.currentSession 中包含 username
- let voteActionResult = await setMyVote(fes.currentSession.username, date, voteVal);
- if (voteActionResult.success) {
- await fes.refreshVotesState(); // 重新獲取並顯示最新的投票資訊
- } else {
- // 顯示錯誤訊息
- updateErrorMessage(voteActionResult.error);
- }
- }
- }
- }
- const daysViewDiv = document.querySelector('section.days-view');
- daysViewDiv.addEventListener("click", handleVoteEvent);
|