Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
labintsev committed Jan 23, 2025
2 parents 4885a9b + f177e88 commit 3aa79a2
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 37 deletions.
18 changes: 18 additions & 0 deletions packages/nextjs/app/lib/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ export async function fetchQuestions() {
}
}

export async function fetchQuestionById(q_id: string) {
console.log("fetching question by id", q_id);
try {
const questions = await sql<Question>`SELECT questions.*, json_agg(choices.*) AS choices_array
FROM questions
LEFT JOIN choices ON questions.id = choices.question_id
WHERE questions.id = ${q_id}
GROUP BY questions.id;
`;

console.log("Questions fetched ", questions.rows);
return questions.rows[0];
} catch (error) {
console.error("Database Error:", error);
throw new Error("Failed to fetch data.");
}
}

export async function createAnswer(formData: FormData) {
const question_id = formData.get("question")?.toString();
const choice_id = formData.get("radio-0")?.toString();
Expand Down
2 changes: 2 additions & 0 deletions packages/nextjs/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const Home: NextPage = () => {
to make the company successful.
</p>
</div>

<div className="flex flex-col bg-base-100 px-10 py-10 text-center items-center max-w-xs rounded-3xl">
<BugAntIcon className="h-8 w-8 fill-secondary" />
<p>
Expand All @@ -39,6 +40,7 @@ const Home: NextPage = () => {
tab.
</p>
</div>

<div className="flex flex-col bg-base-100 px-10 py-10 text-center items-center max-w-xs rounded-3xl">
<MagnifyingGlassIcon className="h-8 w-8 fill-secondary" />
<p>
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/app/seed/placeholder-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const questions = [
},
{
id: "d6e15727-9fe1-4961-8c5b-ea44a9bd81a1",
question: "Who will be Chief Executive Officer in 2025?",
question: "Who should be Chief Executive Officer in 2025?",
is_active: 1,
},
{
Expand Down
56 changes: 56 additions & 0 deletions packages/nextjs/app/voting/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { cookies } from "next/headers";
import { fetchQuestionById, fetchVotingResult } from "~~/app/lib/actions";
import { connectedAddressKey } from "~~/app/lib/definitions";
import { VotingForm } from "~~/app/voting/_components/VotingForm";
import VotingResults from "~~/app/voting/_components/VotingResults";

type Params = {
params: { id: string };
};

export default async function Page({ params }: Params) {
console.log(params);

const activeQuestion = await fetchQuestionById(params.id);
if (activeQuestion === undefined) {
return (
<>
<h1>Error to fetch question</h1>
</>
);
}
const votingResult = await fetchVotingResult(activeQuestion.id);
if (votingResult === undefined) {
return (
<>
<h1>Error to fetch results</h1>
</>
);
}
const cookieStore = cookies();
const connectedAddress = cookieStore.get(connectedAddressKey)?.value;
if (connectedAddress) {
return (
<div className="container mx-auto grid grid-cols-2 gap-4">
<div className="card bg-base-100 border-base-300 border text-primary-content shadow-xl w-128 mx-8 my-4">
<div className="card-body">
<div className="card-title">{activeQuestion.question}</div>
</div>
<VotingForm question={activeQuestion} />
</div>

<div className="card bg-base-100 border-base-300 border text-primary-content shadow-xl w-96 mx-8 my-4">
<div className="card-body">
<h2 className="card-title">Voting results</h2>
<VotingResults voting_result={votingResult} />
</div>
</div>
</div>
);
} else
return (
<>
<h1>Connect to wallet for voting!</h1>
</>
);
}
50 changes: 18 additions & 32 deletions packages/nextjs/app/voting/page.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,24 @@
import { cookies } from "next/headers";
import { fetchQuestions, fetchVotingResult } from "../lib/actions";
import { connectedAddressKey } from "../lib/definitions";
import { VotingForm } from "./_components/VotingForm";
import VotingResults from "./_components/VotingResults";
import Link from "next/link";
import { fetchQuestions } from "~~/app/lib/actions";

export default async function Page() {
const questions = await fetchQuestions();
const activeQuestion = questions[1];
const votingResult = await fetchVotingResult(activeQuestion.id);

const cookieStore = cookies();
const connectedAddress = cookieStore.get(connectedAddressKey)?.value;
if (connectedAddress) {
return (
<div className="container">
<div className="card bg-base-100 border-base-300 border text-primary-content shadow-xl w-96 mx-8 my-4">
<div className="card-body">
<h2 className="card-title">{activeQuestion.question}</h2>
<VotingForm question={activeQuestion} />
</div>
</div>

<div className="card bg-base-100 border-base-300 border text-primary-content shadow-xl w-96 mx-8">
<div className="card-body">
<h2 className="card-title">Voting results</h2>
<VotingResults voting_result={votingResult} />
</div>
return (
<div>
<div className="card bg-base-100 border-base-300 border text-primary-content shadow-xl mx-8 my-4">
<div className="card-body">
<div className="card-title">Questions</div>
<ul>
{questions.map((question, index) => (
<li key={index}>
<div className="m-8">
<Link href={`/voting/${question.id}`}>{question.question}</Link>
</div>
</li>
))}
</ul>
</div>
</div>
);
} else
return (
<>
<h1>Connect to wallet for voting!</h1>
</>
);
</div>
);
}
39 changes: 37 additions & 2 deletions readme_dev.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,45 @@
# Создание приложения для голосования с токенами ERC20.

В этом уроке мы сделаем приложение для управления публичной компанией.
Любой желающий, имеющий токены компании, сможет принять участие в голосовании по ключевым вопросам.
Вес голоса определяется количеством токенов на счету аккаунта в блокчейне.
Вопросы и результаты голосования будут храниться в традиционной базе данных.

## 1 Вступление и определение плана работы

## 2 Разработка моделей данных и наполнение таблиц SQL
В качестве шаблона для нашего приложения мы будем использовать [ScaffoldEth2]().
Фронтенд будет на next.js, база данных для голосования - vercel postgres.
Блокчейн - ethereum sepolia, формат токенов - ERC20.
Откроем VsCode и создадим новое приложение с помощью команды
```
npx create-eth@latest
```
Вводим имя проекта, с помощью стрелок выбираем локальный блокчейн `hardhat`.
Git репозиторий будет инициализирован автоматически.
Также нужно дождаться установки зависимостей.
По завершению должна появиться надпись
```
Congratulations! Your project has been scaffolded! 🎉
```
## 2 Разработка пользовательского интерфейса для голосования

На главной странице добавим карточку, которая будет вести на страницу голосования.

```html
<div className="flex flex-col bg-base-100 px-10 py-10 text-center items-center max-w-xs rounded-3xl">
<StarIcon className="h-8 w-8 fill-secondary" />
<p>
<Link href="/voting" passHref className="link">
Vote
</Link>{" "}
to make the company successful.
</p>
</div>
```



## 3 Разработка интерфейса для голосования
## 3 Разработка моделей данных и наполнение таблиц SQL

## 4 Подключение блокчейна на сервере

Expand Down
3 changes: 1 addition & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2065,7 +2065,6 @@ __metadata:
"@rainbow-me/rainbowkit": 2.1.2
"@tanstack/react-query": ^5.28.6
"@trivago/prettier-plugin-sort-imports": ^4.1.1
"@types/bcrypt": ^5.0.0
"@types/node": ^17.0.35
"@types/nprogress": ^0
"@types/react": ^18.0.9
Expand Down Expand Up @@ -2533,7 +2532,7 @@ __metadata:
languageName: node
linkType: hard

"@types/bcrypt@npm:^5, @types/bcrypt@npm:^5.0.0":
"@types/bcrypt@npm:^5":
version: 5.0.2
resolution: "@types/bcrypt@npm:5.0.2"
dependencies:
Expand Down

0 comments on commit 3aa79a2

Please sign in to comment.