diff --git a/hwe/sammo/API/Vote/AddComment.php b/hwe/sammo/API/Vote/AddComment.php index 03d8a8f9..1122415b 100644 --- a/hwe/sammo/API/Vote/AddComment.php +++ b/hwe/sammo/API/Vote/AddComment.php @@ -7,6 +7,7 @@ use sammo\DB; use sammo\DTO\VoteComment; use sammo\General; use sammo\Session; +use sammo\TimeUtil; use sammo\Validator; class AddComment extends \sammo\BaseAPI @@ -23,7 +24,7 @@ class AddComment extends \sammo\BaseAPI 'voteID', 'text', ])->rule('lengthMin', 'text', 1) - ->rule('int', 'multipleOptions'); + ->rule('int', 'voteID'); if (!$v->validate()) { return $v->errorStr(); @@ -34,21 +35,24 @@ class AddComment extends \sammo\BaseAPI function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) { $voteID = $this->args['voteID']; - $text = $this->args['text']; + $text = mb_substr($this->args['text'], 0, 200); $generalID = $session->generalID; $general = General::createGeneralObjFromDB($generalID, [], 0); $generalName = $general->getName(); $nationID = $general->getNationID(); $nationName = $general->getStaticNation()['name']; + $date = TimeUtil::now(); + $comment = new VoteComment( - voteID: $voteID, - generalID: $generalID, - nationID: $nationID, - nationName: $nationName, - generalName: $generalName, + vote_id: $voteID, + general_id: $generalID, + nation_id: $nationID, + nation_name: $nationName, + general_name: $generalName, text: $text, + date: $date ); $db = DB::db(); diff --git a/hwe/sammo/API/Vote/GetVoteDetail.php b/hwe/sammo/API/Vote/GetVoteDetail.php index a1189cfa..b40daa66 100644 --- a/hwe/sammo/API/Vote/GetVoteDetail.php +++ b/hwe/sammo/API/Vote/GetVoteDetail.php @@ -44,17 +44,31 @@ class GetVoteDetail extends \sammo\BaseAPI $votes = array_map(fn ($arr) => [Json::decode($arr[0]), $arr[1]], $db->queryAllLists( - 'SELECT selection, count(*) AS cnt FROM vote_result WHERE voteID = %i GROUP BY selection', + 'SELECT selection, count(*) AS cnt FROM vote WHERE vote_id = %i GROUP BY selection', $voteID )); - $comments = VoteComment::arrayOf($db->query('SELECT * FROM vote_comment WHERE voteID = %i ORDER BY `id` ASC', $voteID)); + $comments = VoteComment::arrayOf($db->query('SELECT * FROM vote_comment WHERE vote_id = %i ORDER BY `id` ASC', $voteID)); + + $myVote = null; + if($session->isGameLoggedIn()){ + $generalID = $session->generalID; + $rawMyVote = $db->queryFirstField('SELECT selection FROM vote WHERE vote_id = %i AND general_id = %i', $voteID, $generalID); + if($rawMyVote){ + $myVote = Json::decode($rawMyVote); + } + } + + $userCnt = $db->queryFirstField('SELECT count(*) FROM general WHERE npc < 2'); + return [ 'result' => true, 'voteInfo' => $voteInfo, 'votes' => $votes, - 'comments' => $comments + 'comments' => $comments, + 'myVote' => $myVote, + 'userCnt' => $userCnt, ]; } } diff --git a/hwe/sammo/API/Vote/NewVote.php b/hwe/sammo/API/Vote/NewVote.php index 2279d205..f8662462 100644 --- a/hwe/sammo/API/Vote/NewVote.php +++ b/hwe/sammo/API/Vote/NewVote.php @@ -7,6 +7,7 @@ use sammo\DB; use sammo\DTO\VoteInfo; use sammo\KVStorage; use sammo\Session; +use sammo\TimeUtil; use sammo\Util; use sammo\Validator; @@ -47,7 +48,7 @@ class NewVote extends \sammo\BaseAPI return; } - $lastVoteInfo->endDate = new \DateTimeImmutable();; + $lastVoteInfo->endDate = TimeUtil::now(); $voteStor->setValue("vote_{$voteID}", $lastVoteInfo->toArray()); } @@ -68,12 +69,29 @@ class NewVote extends \sammo\BaseAPI $multipleOptions = 0; } - - $now = new \DateTimeImmutable(); + $now = TimeUtil::now(); + /** @var ?string */ $endDate = $this->args['endDate'] ?? null; /** @var string[] */ $options = $this->args['options'] ?? []; + if(!$options){ + return '항목이 없습니다.'; + } + + if($endDate !== null){ + try{ + $oNow = new \DateTimeImmutable($now); + $oEndDate = new \DateTimeImmutable($endDate); + if($oEndDate < $oNow){ + return '종료일이 이미 지났습니다.'; + } + } + catch(\Throwable $e){ + return '종료일이 잘못되었습니다.'.$e->getMessage(); + } + } + $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); @@ -99,6 +117,8 @@ class NewVote extends \sammo\BaseAPI ); $voteStor->setValue("vote_{$voteID}", $voteInfo->toArray()); + $gameStor->setValue('lastVote', $voteID); + $db->update('general', [ 'newvote' => 1 ], true); diff --git a/hwe/sammo/DTO/VoteComment.php b/hwe/sammo/DTO/VoteComment.php index 6d48c1ed..ca44eed6 100644 --- a/hwe/sammo/DTO/VoteComment.php +++ b/hwe/sammo/DTO/VoteComment.php @@ -33,4 +33,6 @@ class VoteComment extends DataTransferObject public string $generalName; public string $text; + + public string $date; } diff --git a/hwe/sammo/DTO/VoteInfo.php b/hwe/sammo/DTO/VoteInfo.php index b63b794a..c2f29b0b 100644 --- a/hwe/sammo/DTO/VoteInfo.php +++ b/hwe/sammo/DTO/VoteInfo.php @@ -9,16 +9,15 @@ use Spatie\DataTransferObject\Casters\ArrayCaster; //https://json2dto.atymic.dev/ - - #[Strict] class VoteInfo extends DataTransferObject { public int $id; public string $title; public int $multipleOptions; - public \DateTimeImmutable $startDate; - public ?\DateTimeImmutable $endDate; + + public string $startDate; + public ?string $endDate; /** @var string[] */ public array $options; diff --git a/hwe/scss/vote.scss b/hwe/scss/vote.scss new file mode 100644 index 00000000..6422943b --- /dev/null +++ b/hwe/scss/vote.scss @@ -0,0 +1,23 @@ +@import '@scss/common/bootstrap5.scss'; +@import "@scss/game_bg.scss"; +@import "@scss/util.scss"; + +@include media-1000px{ + #container { + width: 1000px; + margin: 0 auto; + font-size: 14px; + } +} +@include media-500px{ + body { + overflow-x: hidden; + } + + #container { + width: 500px; + margin: 0 auto; + margin-bottom: 100px; + font-size: 14px; + } +} \ No newline at end of file diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index 64bae991..1c223c5b 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -682,6 +682,7 @@ CREATE TABLE `vote_comment` ( `general_name` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_bin', `nation_name` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_bin', `text` TEXT NOT NULL, + `date` DATETIME NULL DEFAULT NULL, PRIMARY KEY (`id`), INDEX `by_vote` (`vote_id`) ) COLLATE = 'utf8mb4_general_ci' ENGINE = Aria; \ No newline at end of file diff --git a/hwe/ts/PageVote.vue b/hwe/ts/PageVote.vue new file mode 100644 index 00000000..deee2eb7 --- /dev/null +++ b/hwe/ts/PageVote.vue @@ -0,0 +1,502 @@ + + + + + + diff --git a/hwe/ts/build_exports.json b/hwe/ts/build_exports.json index c56c0c6f..7c47de8c 100644 --- a/hwe/ts/build_exports.json +++ b/hwe/ts/build_exports.json @@ -34,6 +34,7 @@ "v_processing": "v_processing.ts", "v_nationBetting": "v_nationBetting.ts", "v_nationGeneral": "v_nationGeneral.ts", - "v_globalDiplomacy": "v_globalDiplomacy" + "v_globalDiplomacy": "v_globalDiplomacy", + "v_vote": "v_vote.ts" } } \ No newline at end of file diff --git a/hwe/ts/defs/API/Vote.ts b/hwe/ts/defs/API/Vote.ts index 3ed5f6d1..54a7712a 100644 --- a/hwe/ts/defs/API/Vote.ts +++ b/hwe/ts/defs/API/Vote.ts @@ -5,7 +5,7 @@ export type VoteInfo = { title: string; multipleOptions: number; startDate: string; - endDate: string; + endDate?: string; options: string[]; } @@ -16,6 +16,7 @@ export type VoteComment = { nationName: string; generalName: string; text: string; + date: string; } export type VoteListResult = ValidResponse & { @@ -26,4 +27,6 @@ export type VoteDetailResult = ValidResponse & { voteInfo: VoteInfo, votes: [number[], number][], comments: VoteComment[], + myVote: null|number[], + userCnt: number, } \ No newline at end of file diff --git a/hwe/ts/utilGame/formatVoteColor.ts b/hwe/ts/utilGame/formatVoteColor.ts new file mode 100644 index 00000000..46c01429 --- /dev/null +++ b/hwe/ts/utilGame/formatVoteColor.ts @@ -0,0 +1,11 @@ +import _colorNames from 'css-color-names'; + +const colorNames = _colorNames as Record; + +const colors: string[] = [ + "red", "orange", "yellow", "green", "blue", "navy", "purple" +].map(color => colorNames[color]); + +export function formatVoteColor(type: number): string { + return colors[type % colors.length]; +} diff --git a/hwe/ts/v_vote.ts b/hwe/ts/v_vote.ts new file mode 100644 index 00000000..646fd734 --- /dev/null +++ b/hwe/ts/v_vote.ts @@ -0,0 +1,15 @@ +import "@scss/vote.scss"; + +import { createApp } from 'vue' +import PageVote from '@/PageVote.vue'; +import { BootstrapVue3, BToastPlugin } from 'bootstrap-vue-3' +import { auto500px } from "./util/auto500px"; +import { htmlReady } from "./util/htmlReady"; +import { insertCustomCSS } from "./util/customCSS"; + +auto500px(); + +htmlReady(() => { + insertCustomCSS(); +}); +createApp(PageVote).use(BootstrapVue3).use(BToastPlugin).mount('#app') \ No newline at end of file diff --git a/hwe/v_vote.php b/hwe/v_vote.php new file mode 100644 index 00000000..6d1822f0 --- /dev/null +++ b/hwe/v_vote.php @@ -0,0 +1,36 @@ +loginGame()->setReadOnly(); +$userID = Session::getUserID(); +$isVoteAdmin = in_array('vote', $session->acl[DB::prefix()] ?? []); +$isVoteAdmin = $isVoteAdmin || $session->userGrade >= 5; +?> + + + + + + + + <?= UniqueConst::$serverName ?>: 설문 조사 + [ + 'serverNick' => DB::prefix(), + 'serverID' => UniqueConst::$serverID, + 'isGameLoggedIn' => $session->isGameLoggedIn(), + 'isVoteAdmin' => $isVoteAdmin, + ]]) ?> + + + + + +
+
+ + + \ No newline at end of file