Skip to content

Commit

Permalink
feat: sorted completed quests and created daily and main quest routes #1
Browse files Browse the repository at this point in the history
 (#54)

* add sorting for completed

* Create quest.go

To store Quest Structs

* Create Quests.go

* Update routes.go

* Update routes.go

* Update and rename Quests.go to quests.go

* Update quest.go

* Delete quest.go

* Update quests.go

Updates to quests route

* Update quests.go

Changed route name to follow other routes pattern.

* Update quests.go

* Update Quests.js to consume mainQuests and dailyQuests routes

* Update Quests.js

* Display local quests for now

---------

Co-authored-by: Otaiki1 <otaikisadiq2804@gmail.com>
Co-authored-by: Brandon Roberts <brandonjroberts22@gmail.com>
Co-authored-by: Brandon R <54774639+b-j-roberts@users.noreply.github.com>
  • Loading branch information
4 people authored Apr 22, 2024
1 parent c8c6edc commit 3fa4ac8
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 24 deletions.
76 changes: 76 additions & 0 deletions backend/routes/quests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package routes

import (
"context"
"encoding/json"
"log"
"net/http"

"github.com/keep-starknet-strange/art-peace/backend/core"
)

// the Quest struct will represent the structure for both Daily and Main Quests data
type Quest struct {
Key int `json:"key"`
Name string `json:"name"`
Description string `json:"description"`
Reward int `json:"reward"`
DayIndex int `json:"dayIndex,omitempty"` // Only for daily quests
}

func InitQuestsRoutes() {
http.HandleFunc("/getDailyQuests", GetDailyQuests)
http.HandleFunc("/getMainQuests", GetMainQuests)
}

// Query dailyQuests
func GetDailyQuests(w http.ResponseWriter, r *http.Request) {
query := `SELECT key, name, description, reward, dayIndex FROM DailyQuests ORDER BY dayIndex ASC`
handleQuestQuery(w, r, query)
}

// Query mainQuest
func GetMainQuests(w http.ResponseWriter, r *http.Request) {
query := `SELECT key, name, description, reward FROM MainQuests`
handleQuestQuery(w, r, query)
}

func handleQuestQuery(w http.ResponseWriter, r *http.Request, query string) {
var quests []Quest
rows, err := core.ArtPeaceBackend.Databases.Postgres.Query(context.Background(), query)
if err != nil {
http.Error(w, "Database query failed: "+err.Error(), http.StatusInternalServerError)
return
}
defer rows.Close()

for rows.Next() {
var q Quest
if err := rows.Scan(&q.Key, &q.Name, &q.Description, &q.Reward, &q.DayIndex); err != nil {
log.Printf("Error scanning row: %v", err)
continue // Log and continue to process other rows
}
quests = append(quests, q)
}
if err := rows.Err(); err != nil {
log.Printf("Error during rows iteration: %v", err)
http.Error(w, "Error processing data: "+err.Error(), http.StatusInternalServerError)
return
}

setupCORS(&w, r)
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(quests); err != nil {
http.Error(w, "Error encoding response: "+err.Error(), http.StatusInternalServerError)
}
}

// CORS setup
func setupCORS(w *http.ResponseWriter, r *http.Request) {
(*w).Header().Set("Access-Control-Allow-Origin", "*")
if r.Method == "OPTIONS" {
(*w).Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
(*w).Header().Set("Access-Control-Allow-Headers", "Content-Type")
(*w).WriteHeader(http.StatusOK)
}
}
1 change: 1 addition & 0 deletions backend/routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ func InitRoutes() {
InitTemplateRoutes()
InitUserRoutes()
InitContractRoutes()
InitQuestsRoutes()
InitColorsRoutes()
}
116 changes: 92 additions & 24 deletions frontend/src/tabs/quests/Quests.js
Original file line number Diff line number Diff line change
@@ -1,74 +1,142 @@
import React from 'react'
import './Quests.css';
import BasicTab from '../BasicTab.js';
import QuestItem from './QuestItem.js';
import React, { useState, useEffect } from "react";
import "./Quests.css";
import BasicTab from "../BasicTab.js";
import QuestItem from "./QuestItem.js";

const Quests = props => {
const Quests = (props) => {
const [dailyQuests, setDailyQuests] = useState([]);
const [mainQuests, setMainQuests] = useState([]);

useEffect(() => {
const fetchQuests = async () => {
try {
// Fetching daily quests from backend
const dailyResponse = await fetch('http://localhost:8080/getDailyQuests');
const dailyData = await dailyResponse.json();
setDailyQuests(dailyData);

// Fetching main quests from backend
const mainResponse = await fetch('http://localhost:8080/getMainQuests');
const mainData = await mainResponse.json();
setMainQuests(mainData);
} catch (error) {
console.error('Failed to fetch quests', error);
}
};

fetchQuests();
}, []);
// TODO: Main quests should be scrollable
// TODO: Main quests should be moved to the bottom on complete
// TODO: Pull quests from backend
// TODO: Links in descriptions
const dailyQuests = [



const localDailyQuests = [
{
title: "Place 10 pixels",
description: "Add 10 pixels on the canvas",
reward: "3",
status: "completed"
status: "completed",
},
{
title: "Build a template",
description: "Create a template for the community to use",
reward: "3",
status: "claim"
status: "claim",
},
{
title: "Deploy a Memecoin",
description: "Create an Unruggable memecoin",
reward: "10",
status: "completed"
}
]
status: "completed",
},
];

const mainQuests = [
const localMainQuests = [
{
title: "Tweet #art/peace",
description: "Tweet about art/peace using the hashtag & addr",
reward: "10",
status: "incomplete"
status: "incomplete",
},
{
title: "Place 100 pixels",
description: "Add 100 pixels on the canvas",
reward: "10",
status: "completed"
status: "completed",
},
{
title: "Mint an art/peace NFT",
description: "Mint an NFT using the art/peace theme",
reward: "5",
status: "incomplete"
status: "incomplete",
},
];

const sortByCompleted = (arr) => {
if (!arr) return [];
const newArray = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i].status == "completed") {
newArray.push(arr[i]);
} else {
newArray.unshift(arr[i]);
}
}
]
return newArray;
};

// TODO: Icons for each tab?
return (
<BasicTab title="Quests">
<div style={{height: '70vh', overflowY: 'scroll'}}>
<div style={{display: 'flex', alignItems: 'center'}}>
<div style={{ height: "70vh", overflowY: "scroll" }}>
<div style={{ display: "flex", alignItems: "center" }}>
<h2 className="Quests__item__header">Dailys</h2>
<p style={{fontSize: "1rem", marginLeft: "1rem"}}>{props.timeLeftInDay}</p>
</div>
{dailyQuests.map((quest, index) => (
<QuestItem key={index} title={quest.title} description={quest.description} reward={quest.reward} status={quest.status} />
{sortByCompleted(dailyQuests).map((quest, index) => (
<QuestItem
key={index}
title={quest.title}
description={quest.description}
reward={quest.reward}
status={quest.status}
/>
))}

{sortByCompleted(localDailyQuests).map((quest, index) => (
<QuestItem
key={index}
title={quest.title}
description={quest.description}
reward={quest.reward}
status={quest.status}
/>
))}

<h2 className="Quests__item__header">Main</h2>
{mainQuests.map((quest, index) => (
<QuestItem key={index} title={quest.title} description={quest.description} reward={quest.reward} status={quest.status} />
{sortByCompleted(mainQuests).map((quest, index) => (
<QuestItem
key={index}
title={quest.title}
description={quest.description}
reward={quest.reward}
status={quest.status}
/>
))}
{sortByCompleted(localMainQuests).map((quest, index) => (
<QuestItem
key={index}
title={quest.title}
description={quest.description}
reward={quest.reward}
status={quest.status}
/>
))}
</div>
</BasicTab>
);
}
};

export default Quests;

0 comments on commit 3fa4ac8

Please sign in to comment.