From a69c0d4b5d015995bd1762b846d7402c665ec585 Mon Sep 17 00:00:00 2001
From: Koloskov Konstantin <408827@niuitmo.ru>
Date: Tue, 16 Dec 2025 20:13:17 +0300
Subject: [PATCH 1/2] add required balloons counter
---
frontend/src/components/ProblemList.tsx | 64 +++-
frontend/src/index.css | 447 +++++++++++++++---------
frontend/src/pages/ActiveBalloons.tsx | 143 ++++----
3 files changed, 402 insertions(+), 252 deletions(-)
diff --git a/frontend/src/components/ProblemList.tsx b/frontend/src/components/ProblemList.tsx
index 7ba93d1..474ab34 100644
--- a/frontend/src/components/ProblemList.tsx
+++ b/frontend/src/components/ProblemList.tsx
@@ -2,25 +2,57 @@ import { useMemo } from 'react';
import { Balloon, Contest, Problem } from '../types';
import ProblemBox from './ProblemBox';
-const ProblemBlock = ({ problem, solves }: { problem: Problem, solves: number }) => {
- return useMemo(() => (
-
- ), [problem, solves]);
+const ProblemBlock = ({ problem, solves, required, solved = false }: {
+ problem: Problem,
+ solves: number,
+ required: number,
+ solved?: boolean
+}) => {
+ const getUrgencyLevel = (count: number): string => {
+ if (count > 100) return '5';
+ if (count > 65) return '4';
+ if (count > 30) return '3';
+ if (count > 15) return '2';
+ if (count > 0) return '1';
+ return '';
+ };
+ return useMemo(() => (
+
+
+
+ {solves}
+
+ {required > 0 && (
+
+ {required}
+
+ )}
+
+ ), [problem, solves, required, solved]);
};
const ProblemList = ({ contest, balloons }: { contest: Contest, balloons: Balloon[] }) => {
- return useMemo(() => (
-
- {contest.problems.map(problem => (
-
b.problemId === problem.id).length} />
- ))}
-
- ), [contest, balloons]);
+ return useMemo(() => (
+
+ {contest.problems.map(problem => {
+ const solvedBalloons = balloons.filter(b => b.problemId === problem.id);
+ const requiredCount = solvedBalloons.filter(
+ balloon => balloon.takenBy === null && !balloon.delivered
+ ).length;
+
+ return (
+
0}
+ />
+ );
+ })}
+
+
+ ), [contest, balloons]);
};
export default ProblemList;
diff --git a/frontend/src/index.css b/frontend/src/index.css
index 85bdcb9..76bb12e 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -1,334 +1,461 @@
:root {
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
- line-height: 1.5;
- font-weight: 400;
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
- color: #213547;
- background-color: #ffffff;
+ color: #213547;
+ background-color: #ffffff;
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
}
body {
- margin: 0;
- min-width: 320px;
+ margin: 0;
+ min-width: 320px;
}
#root {
- /*margin: 0 auto;*/
- padding: 1em;
- /*max-width: 800px;*/
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
+ /*margin: 0 auto;*/
+ padding: 1em;
+ /*max-width: 800px;*/
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ box-sizing: border-box;
}
nav>*,
footer>* {
- display: inline-block;
- margin-right: 1em;
+ display: inline-block;
+ margin-right: 1em;
}
nav>a.active {
- font-weight: bold;
- color: #000;
+ font-weight: bold;
+ color: #000;
}
a {
- user-select: none;
- color: #646cff;
- text-decoration: inherit;
- cursor: pointer;
+ user-select: none;
+ color: #646cff;
+ text-decoration: inherit;
+ cursor: pointer;
}
a:hover {
- color: #747bff;
+ color: #747bff;
}
main {
- margin: 1em 0;
+ margin: 1em 0;
}
input[type=text],
input[type=password] {
- display: block;
- font: inherit;
- margin-bottom: 0.7em;
+ display: block;
+ font: inherit;
+ margin-bottom: 0.7em;
}
button {
- border-radius: 8px;
- border: 1px solid transparent;
- padding: 0.4em 1.2em;
- font: inherit;
- font-weight: 500;
- background-color: #646cff;
- color: #fff;
- cursor: pointer;
- transition: border-color 0.25s;
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.4em 1.2em;
+ font: inherit;
+ font-weight: 500;
+ background-color: #646cff;
+ color: #fff;
+ cursor: pointer;
+ transition: border-color 0.25s;
}
button:hover {
- background-color: #747bff;
+ background-color: #747bff;
}
button:focus,
button:focus-visible {
- outline: 4px auto -webkit-focus-ring-color;
+ outline: 4px auto -webkit-focus-ring-color;
}
.form-error {
- color: red;
- margin-bottom: 0.7em;
+ color: red;
+ margin-bottom: 0.7em;
}
footer {
- margin-top: auto;
+ margin-top: auto;
}
table {
- border-spacing: 0;
- border-collapse: collapse;
+ border-spacing: 0;
+ border-collapse: collapse;
}
th, td {
- padding: 0.3em;
- padding-right: 3em;
+ padding: 0.3em;
+ padding-right: 3em;
}
th {
- text-align: left;
- border-bottom: 1px solid #000;
+ text-align: left;
+ border-bottom: 1px solid #000;
}
.sr-only {
- position: absolute;
- left: -10000px;
- top: auto;
- width: 1px;
- height: 1px;
- overflow: hidden;
+ position: absolute;
+ left: -10000px;
+ top: auto;
+ width: 1px;
+ height: 1px;
+ overflow: hidden;
}
.access-link.disabled {
- color: #777;
- cursor: not-allowed;
+ color: #777;
+ cursor: not-allowed;
}
.crossed {
- position: relative;
- overflow: hidden;
+ position: relative;
+ overflow: hidden;
}
.crossed:before,
.crossed:after {
- position: absolute;
- content: '';
- background: rgba(0, 0, 0, 0.2);
- display: block;
- width: 100%;
- height: 0.2em;
- -webkit-transform: rotate(-45deg);
- transform: rotate(-45deg);
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- margin: auto;
+ position: absolute;
+ content: '';
+ background: rgba(0, 0, 0, 0.2);
+ display: block;
+ width: 100%;
+ height: 0.2em;
+ -webkit-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ margin: auto;
}
.crossed:after {
- -webkit-transform: rotate(45deg);
- transform: rotate(45deg);
-}
-
-.problem-list {
- display: flex;
- gap: 0.5em;
+ -webkit-transform: rotate(45deg);
+ transform: rotate(45deg);
}
.problem {
- font-size: 1.6em;
- width: 1.2em;
- height: 1.2em;
- line-height: 1.2em;
- text-align: center;
- vertical-align: middle;
- border: 1px solid black;
-}
-
-.problem-solves {
- font-size: 0.9em;
- color: #777;
- text-align: center;
+ font-size: 1.6em;
+ font-weight: 600;
+ padding: 2px;
+ width: 1.2em;
+ height: 1.2em;
+ line-height: 1.2em;
+ text-align: center;
+ vertical-align: middle;
+ border: 1px solid #e2e8f0;
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ box-shadow: 0 1px 6px rgba(0, 0, 0, 0.04);
+ user-select: none;
+ cursor: default;
}
.balloons-main {
- width: max-content;
- max-width: none;
+ width: max-content;
+ max-width: none;
}
.contest-name {
- margin-bottom: 0.7em;
+ margin-bottom: 0.7em;
}
.balloon-list {
- display: table;
+ display: table;
}
.balloon-row {
- display: table-row;
+ display: table-row;
}
.balloon-row > * {
- display: table-cell;
- vertical-align: middle;
+ display: table-cell;
+ vertical-align: middle;
}
.balloon-row > *:not(:first-child) {
- padding: 0 0.3em;
+ padding: 0 0.3em;
}
.balloon-row .fts,
.balloon-row .team-place,
.balloon-row .actions {
- font-size: 1.6em;
+ font-size: 1.6em;
}
.balloon-row .actions a {
- margin-right: 0.3em;
+ margin-right: 0.3em;
}
.balloon-row .team-name {
- font-size: 0.75em;
- min-width: 250px;
- max-width: 30vw;
+ font-size: 0.75em;
+ min-width: 250px;
+ max-width: 30vw;
}
.standings th,
.standings td {
- padding: 0.1em;
+ padding: 0.1em;
}
.standings .team-name,
.standings .team-place {
- padding: 0.1em 0.8em;
+ padding: 0.1em 0.8em;
}
.standings th:not(.team-name) {
- text-align: center;
+ text-align: center;
}
.standings td {
- padding: 0.1em;
+ padding: 0.1em;
}
.standings td.team-place {
- font-size: 1.6em;
+ font-size: 1.6em;
}
.standings td.team-name {
- font-size: 0.75em;
- min-width: 250px;
- max-width: 30vw;
+ font-size: 0.75em;
+ min-width: 250px;
+ max-width: 30vw;
}
.standings th.team-problem,
.standings td.team-problem {
- min-width: 2.1em;
+ min-width: 2.1em;
}
.hall-dropdown {
- display: inline-block;
- position: relative;
+ display: inline-block;
+ position: relative;
}
.hall-dropdown-toggle {
- color: #646cff;
- cursor: pointer;
+ color: #646cff;
+ cursor: pointer;
}
.hall-dropdown-toggle:hover {
- color: #747bff;
+ color: #747bff;
}
.dropdown-arrow:before {
- content: '▼';
+ content: '▼';
}
/* .hall-dropdown-toggle ~ .hall-dropdown-menu + .dropdown-arrow,*/
.hall-dropdown-toggle:has(~ .hall-dropdown-menu) .dropdown-arrow:before {
- content: '▲';
+ content: '▲';
}
.dropdown-arrow {
- margin-left: 0.3em;
- font-size: 0.8em;
+ margin-left: 0.3em;
+ font-size: 0.8em;
}
.hall-dropdown-menu {
- position: absolute;
- top: 100%;
- left: 0;
- margin-top: 0.25rem;
- padding-left: 0;
- min-width: 150px;
- background: white;
- border: 1px solid #ccc;
- border-radius: 4px;
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
- z-index: 1000;
+ position: absolute;
+ top: 100%;
+ left: 0;
+ margin-top: 0.25rem;
+ padding-left: 0;
+ min-width: 150px;
+ background: white;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ z-index: 1000;
}
.hall-dropdown-menu li {
- box-sizing: border-box;
- display: block;
- width: 100%;
- padding: 0.5rem 0.75rem;
- text-align: left;
- border: none;
- background: none;
- cursor: pointer;
- color: inherit;
+ box-sizing: border-box;
+ display: block;
+ width: 100%;
+ padding: 0.5rem 0.75rem;
+ text-align: left;
+ border: none;
+ background: none;
+ cursor: pointer;
+ color: inherit;
}
.hall-dropdown-menu li:hover {
- background: #f5f5f5;
+ background: #f5f5f5;
}
.hall-dropdown-menu li.active {
- font-weight: bold;
+ font-weight: bold;
}
.hall-dropdown-menu li:first-child {
- border-top-left-radius: 4px;
- border-top-right-radius: 4px;
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
}
.hall-dropdown-menu li:last-child {
- border-bottom-left-radius: 4px;
- border-bottom-right-radius: 4px;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
}
.connection-status {
- position: fixed;
- top: 4em;
- right: 1em;
- color: white;
- text-align: center;
- padding: 0.5em;
- z-index: 1000;
+ position: fixed;
+ top: 4em;
+ right: 1em;
+ color: white;
+ text-align: center;
+ padding: 0.5em;
+ z-index: 1000;
}
.connection-status.in-progress {
- background-color: #6b9fff;
+ background-color: #6b9fff;
}
.connection-status.lost {
- background-color: #ff6b6b;
+ background-color: #ff6b6b;
+}
+
+.problem-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ padding: 12px;
+ justify-content: start;
+ align-items: flex-start;
+ max-width: 100%;
+ overflow-x: auto;
+ min-height: 120px;
+}
+
+.problem-list > div {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ min-width: 40px;
+ padding: 4px 4px;
+ border-radius: 10px;
+ background: #f8f9fa;
+ border: 1px solid #e9ecef;
+ transition: all 0.2s ease;
+ position: relative;
+}
+
+.problem-list > div:hover {
+ background: #e9ecef;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+
+.problem-solves {
+ font-size: 18px;
+ font-weight: 700;
+ color: #2d3436;
+ margin-bottom: 2px;
+}
+
+.balloons-require {
+ font-size: 14px;
+ font-weight: 600;
+ padding: 2px 6px;
+ border-radius: 12px;
+ min-width: 24px;
+ text-align: center;
+ transition: all 0.2s ease;
+}
+
+.balloons-require:not(:empty) {
+ background: #ff6b6b;
+ color: white;
+ animation: pulse 2s infinite;
+}
+
+.balloons-require:empty {
+ display: none;
+}
+
+.balloons-require[urgency-level="1"] {
+ background: #ffa8a8;
+}
+
+.balloons-require[urgency-level="2"] {
+ background: #ff6b6b;
+}
+
+.balloons-require[urgency-level="3"] {
+ background: #fa5252;
+}
+
+.balloons-require[urgency-level="4"] {
+ background: #e03131;
+}
+
+.balloons-require[urgency-level="5"] {
+ background: #c92a2a;
+}
+
+@keyframes pulse {
+ 0% {
+ box-shadow: 0 0 0 0 rgba(255, 107, 107, 0.4);
+ }
+ 70% {
+ box-shadow: 0 0 0 6px rgba(255, 107, 107, 0);
+ }
+ 100% {
+ box-shadow: 0 0 0 0 rgba(255, 107, 107, 0);
+ }
+}
+
+@media (max-width: 480px) {
+ .problem-list {
+ gap: 6px;
+ padding: 8px;
+ }
+
+ .problem-list > div {
+ min-width: 44px;
+ padding: 6px 3px;
+ }
+
+ .problem-solves {
+ font-size: 16px;
+ }
+
+ .balloons-require {
+ font-size: 12px;
+ padding: 1px 5px;
+ }
+}
+
+.problem-list > div.solved-problem::after {
+ content: "✓";
+ position: absolute;
+ top: -6px;
+ right: -6px;
+ background: #51cf66;
+ color: white;
+ width: 16px;
+ height: 16px;
+ border-radius: 50%;
+ font-size: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: bold;
}
\ No newline at end of file
diff --git a/frontend/src/pages/ActiveBalloons.tsx b/frontend/src/pages/ActiveBalloons.tsx
index 29e4665..2ae861d 100644
--- a/frontend/src/pages/ActiveBalloons.tsx
+++ b/frontend/src/pages/ActiveBalloons.tsx
@@ -9,96 +9,87 @@ import { RootState } from '../store/store';
import { useWebSocket } from '../contexts/WebSocketContext';
import { useFilteredBalloons } from '../hooks/useFilteredBalloons';
import { useTranslation } from 'react-i18next';
-import { useContestMaps } from '../hooks/useContestMaps';
-import { Balloon, Team } from '../types';
-
-function teamId(balloon: Balloon, teamMap: Record): string {
- return teamMap[balloon.teamId]?.displayName ?? balloon.teamId;
-}
const BalloonsView = ({ infoHolder }: { infoHolder: InfoHolder }) => {
- const { t } = useTranslation();
- const ws = useWebSocket();
- const contest = useSelector((state: RootState) => state.contest);
- const filteredBalloons = useFilteredBalloons();
- const { teamMap } = useContestMaps(contest);
+ const { t } = useTranslation();
+ const ws = useWebSocket();
+ const contest = useSelector((state: RootState) => state.contest);
+ const filteredBalloons = useFilteredBalloons();
- const myBalloons = useMemo(() => {
- return filteredBalloons
- .filter(balloon => balloon.takenBy === infoHolder.info.login && !balloon.delivered)
- .sort((a, b) => teamId(a, teamMap).localeCompare(teamId(b, teamMap)));
- }, [filteredBalloons, infoHolder.info.login, teamMap]);
+ const myBalloons = useMemo(() => {
+ return filteredBalloons.filter(balloon => balloon.takenBy === infoHolder.info.login && !balloon.delivered);
+ }, [filteredBalloons, infoHolder.info.login]);
- const queuedBalloons = useMemo(() => {
- return filteredBalloons.filter(balloon => balloon.takenBy === null && !balloon.delivered);
- }, [filteredBalloons]);
+ const queuedBalloons = useMemo(() => {
+ return filteredBalloons.filter(balloon => balloon.takenBy === null && !balloon.delivered);
+ }, [filteredBalloons]);
- const takenBalloons = useMemo(() => {
- return filteredBalloons.filter(balloon => balloon.takenBy !== null && !balloon.delivered);
- }, [filteredBalloons]);
+ const takenBalloons = useMemo(() => {
+ return filteredBalloons.filter(balloon => balloon.takenBy !== null && !balloon.delivered);
+ }, [filteredBalloons]);
- return (
-
- {t('balloons.title')}
- {contest.name}
-
- (
- <>
- ws?.send(JSON.stringify({ type: 'deliverBalloon', runId: balloon.runId }))}>
- {t('balloons.actions.done')}
-
- ws?.send(JSON.stringify({ type: 'dropBalloon', runId: balloon.runId }))}>
- {t('balloons.actions.drop')}
-
- >
- )}
- />
- (
- ws?.send(JSON.stringify({ type: 'takeBalloon', runId: balloon.runId }))}>
- {t('balloons.actions.take')}
-
- )}
- />
- (
-
+ return (
+
+ {t('balloons.title')}
+ {contest.name}
+
+ (
+ <>
+ ws?.send(JSON.stringify({ type: 'deliverBalloon', runId: balloon.runId }))}>
+ {t('balloons.actions.done')}
+
+ ws?.send(JSON.stringify({ type: 'dropBalloon', runId: balloon.runId }))}>
+ {t('balloons.actions.drop')}
+
+ >
+ )}
+ />
+ (
+ ws?.send(JSON.stringify({ type: 'takeBalloon', runId: balloon.runId }))}>
+ {t('balloons.actions.take')}
+
+ )}
+ />
+ (
+
{t('balloons.carriedBy')}
- {balloon.takenBy}
+ {balloon.takenBy}
- )}
- />
-
- );
+ )}
+ />
+
+ );
};
const ActiveBalloons = ({ infoHolder }: { infoHolder: InfoHolder }) => {
- const { t } = useTranslation();
+ const { t } = useTranslation();
- if (!infoHolder.info.login) {
- return ;
- }
+ if (!infoHolder.info.login) {
+ return ;
+ }
- if (!infoHolder.info.canAccess) {
- return (
-
- );
- }
+ if (!infoHolder.info.canAccess) {
+ return (
+
+ );
+ }
- return ;
+ return ;
};
export default ActiveBalloons;
From e5840ccff4152b0d5c236d5abe0586ea48136a64 Mon Sep 17 00:00:00 2001
From: Nikita Sychev
Date: Wed, 10 Dec 2025 13:36:22 +0500
Subject: [PATCH 2/2] Sort carrying balloons in the order of team places
instead of by time
---
frontend/src/pages/ActiveBalloons.tsx | 143 ++++++++++++++------------
1 file changed, 76 insertions(+), 67 deletions(-)
diff --git a/frontend/src/pages/ActiveBalloons.tsx b/frontend/src/pages/ActiveBalloons.tsx
index 2ae861d..29e4665 100644
--- a/frontend/src/pages/ActiveBalloons.tsx
+++ b/frontend/src/pages/ActiveBalloons.tsx
@@ -9,87 +9,96 @@ import { RootState } from '../store/store';
import { useWebSocket } from '../contexts/WebSocketContext';
import { useFilteredBalloons } from '../hooks/useFilteredBalloons';
import { useTranslation } from 'react-i18next';
+import { useContestMaps } from '../hooks/useContestMaps';
+import { Balloon, Team } from '../types';
+
+function teamId(balloon: Balloon, teamMap: Record): string {
+ return teamMap[balloon.teamId]?.displayName ?? balloon.teamId;
+}
const BalloonsView = ({ infoHolder }: { infoHolder: InfoHolder }) => {
- const { t } = useTranslation();
- const ws = useWebSocket();
- const contest = useSelector((state: RootState) => state.contest);
- const filteredBalloons = useFilteredBalloons();
+ const { t } = useTranslation();
+ const ws = useWebSocket();
+ const contest = useSelector((state: RootState) => state.contest);
+ const filteredBalloons = useFilteredBalloons();
+ const { teamMap } = useContestMaps(contest);
- const myBalloons = useMemo(() => {
- return filteredBalloons.filter(balloon => balloon.takenBy === infoHolder.info.login && !balloon.delivered);
- }, [filteredBalloons, infoHolder.info.login]);
+ const myBalloons = useMemo(() => {
+ return filteredBalloons
+ .filter(balloon => balloon.takenBy === infoHolder.info.login && !balloon.delivered)
+ .sort((a, b) => teamId(a, teamMap).localeCompare(teamId(b, teamMap)));
+ }, [filteredBalloons, infoHolder.info.login, teamMap]);
- const queuedBalloons = useMemo(() => {
- return filteredBalloons.filter(balloon => balloon.takenBy === null && !balloon.delivered);
- }, [filteredBalloons]);
+ const queuedBalloons = useMemo(() => {
+ return filteredBalloons.filter(balloon => balloon.takenBy === null && !balloon.delivered);
+ }, [filteredBalloons]);
- const takenBalloons = useMemo(() => {
- return filteredBalloons.filter(balloon => balloon.takenBy !== null && !balloon.delivered);
- }, [filteredBalloons]);
+ const takenBalloons = useMemo(() => {
+ return filteredBalloons.filter(balloon => balloon.takenBy !== null && !balloon.delivered);
+ }, [filteredBalloons]);
- return (
-
- {t('balloons.title')}
- {contest.name}
-
- (
- <>
- ws?.send(JSON.stringify({ type: 'deliverBalloon', runId: balloon.runId }))}>
- {t('balloons.actions.done')}
-
- ws?.send(JSON.stringify({ type: 'dropBalloon', runId: balloon.runId }))}>
- {t('balloons.actions.drop')}
-
- >
- )}
- />
- (
- ws?.send(JSON.stringify({ type: 'takeBalloon', runId: balloon.runId }))}>
- {t('balloons.actions.take')}
-
- )}
- />
- (
-
+ return (
+
+ {t('balloons.title')}
+ {contest.name}
+
+ (
+ <>
+ ws?.send(JSON.stringify({ type: 'deliverBalloon', runId: balloon.runId }))}>
+ {t('balloons.actions.done')}
+
+ ws?.send(JSON.stringify({ type: 'dropBalloon', runId: balloon.runId }))}>
+ {t('balloons.actions.drop')}
+
+ >
+ )}
+ />
+ (
+ ws?.send(JSON.stringify({ type: 'takeBalloon', runId: balloon.runId }))}>
+ {t('balloons.actions.take')}
+
+ )}
+ />
+ (
+
{t('balloons.carriedBy')}
- {balloon.takenBy}
+ {balloon.takenBy}
- )}
- />
-
- );
+ )}
+ />
+
+ );
};
const ActiveBalloons = ({ infoHolder }: { infoHolder: InfoHolder }) => {
- const { t } = useTranslation();
+ const { t } = useTranslation();
- if (!infoHolder.info.login) {
- return ;
- }
+ if (!infoHolder.info.login) {
+ return ;
+ }
- if (!infoHolder.info.canAccess) {
- return (
-
- );
- }
+ if (!infoHolder.info.canAccess) {
+ return (
+
+ );
+ }
- return ;
+ return ;
};
export default ActiveBalloons;