Skip to content

Commit

Permalink
Game dlcs (#924)
Browse files Browse the repository at this point in the history
* chore: add game_dlcs table

To store dlcs in the future

* ci: manage game dlcs

* ci: automate dlc management

* ci: dlcs_as_json sql

* feat: api route for dlcs

* chore: add dlc api in store

* feat: DLC page
  • Loading branch information
jy95 authored Nov 1, 2024
1 parent 4249929 commit b449a3c
Show file tree
Hide file tree
Showing 17 changed files with 322 additions and 16 deletions.
29 changes: 29 additions & 0 deletions .github/ISSUE_TEMPLATE/09-manage-game-dlcs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: 🧩 Manage dlcs
description: Manage dlcs about a given game into the database
title: '[MANAGE_DLCS] '
labels: ["database","[MANAGE_DLCS]"]
body:
- type: markdown
attributes:
value: |
Please fill in the following fields to manage dlcs about a given game into the database !
- type: input
id: gameID
attributes:
label: ID of the game
description: What is the ID of the game (playlistId or videoId) ?
placeholder: ex. PLRfhDHeBTBJ6POjMkqqeCliq5jrCWagcg
validations:
required: true
- type: textarea
id: dlcs_textarea
attributes:
label: Dlcs
description: Put the ID of each dlc (playlistId or videoId) in the order you want them to be
placeholder: |
ID_1
ID_2
ID_3
...
validations:
required: true
4 changes: 4 additions & 0 deletions .github/workflows/issuesAutomatedTasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
contains(github.event.issue.labels.*.name, '[ADD_COVER]') && 'ADD_COVER' ||
contains(github.event.issue.labels.*.name, '[ADD_SERIE]') && 'ADD_SERIE' ||
contains(github.event.issue.labels.*.name, '[MANAGE_SERIE]') && 'MANAGE_SERIE' ||
contains(github.event.issue.labels.*.name, '[MANAGE_DLCS]') && 'MANAGE_DLCS' ||
'UNKNOWN' }}
run: echo "issue_type=${{ env.ISSUE_TYPE }}" >> $GITHUB_OUTPUT
- id: set_template_file
Expand Down Expand Up @@ -54,6 +55,9 @@ jobs:
MANAGE_SERIE)
echo "template_file=08-manage-serie.yml" >> $GITHUB_OUTPUT
;;
MANAGE_DLCS)
echo "template_file=09-manage-game-dlcs.yml" >> $GITHUB_OUTPUT
;;
esac
process_issue:
runs-on: ubuntu-latest
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/refreshJSONFiles.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@ jobs:
- genres.json
- planning.json
- stats.json
- past-planning.json
- past-planning.json
- dlcs.json
draft: false
Binary file modified GamesPassionFR.db
Binary file not shown.
2 changes: 1 addition & 1 deletion GamesPassionFR.sqbpro
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8"?><sqlb_project><db path="GamesPassionFR.db" readonly="0" foreign_keys="1" case_sensitive_like="0" temp_store="0" wal_autocheckpoint="1000" synchronous="2"/><attached/><window><main_tabs open="structure browser pragmas query" current="0"/></window><tab_structure><column_width id="0" width="300"/><column_width id="1" width="0"/><column_width id="2" width="100"/><column_width id="3" width="2450"/><column_width id="4" width="0"/><expanded_item id="0" parent="1"/><expanded_item id="1" parent="1"/><expanded_item id="2" parent="1"/><expanded_item id="3" parent="1"/></tab_structure><tab_browse><table title="games" custom_title="0" dock_id="2" table="4,5:maingames"/><table title="games_schedules" custom_title="0" dock_id="1" table="4,15:maingames_schedules"/><dock_state state="000000ff00000000fd00000001000000020000043c000002b6fc0100000001fc000000000000043c0000011b00fffffffa000000000100000002fb000000160064006f0063006b00420072006f00770073006500310100000000ffffffff0000011b00fffffffb000000160064006f0063006b00420072006f00770073006500320100000000ffffffff0000011b00ffffff0000043c0000000000000004000000040000000800000008fc00000000"/><default_encoding codec=""/><browse_table_settings><table schema="main" name="all_series_as_json" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths/><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="backlog" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="31"/><column index="2" value="300"/><column index="3" value="58"/><column index="4" value="257"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="games" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort><column index="0" mode="0"/></sort><column_widths><column index="1" value="31"/><column index="2" value="93"/><column index="3" value="273"/><column index="4" value="300"/><column index="5" value="85"/><column index="6" value="70"/><column index="7" value="58"/><column index="8" value="70"/><column index="9" value="41"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="games_available_time" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="40"/><column index="2" value="53"/><column index="3" value="53"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="games_genres" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="41"/><column index="2" value="42"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="games_schedules" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="31"/><column index="2" value="85"/><column index="3" value="85"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table></browse_table_settings></tab_browse><tab_sql><sql name="SQL 1"></sql><current_tab id="0"/></tab_sql></sqlb_project>
<?xml version="1.0" encoding="UTF-8"?><sqlb_project><db path="GamesPassionFR.db" readonly="0" foreign_keys="1" case_sensitive_like="0" temp_store="0" wal_autocheckpoint="1000" synchronous="2"/><attached/><window><main_tabs open="structure browser pragmas query" current="3"/></window><tab_structure><column_width id="0" width="300"/><column_width id="1" width="0"/><column_width id="2" width="100"/><column_width id="3" width="3140"/><column_width id="4" width="0"/><expanded_item id="0" parent="1"/><expanded_item id="1" parent="1"/><expanded_item id="2" parent="1"/><expanded_item id="3" parent="1"/></tab_structure><tab_browse><table title="all_series_as_json" custom_title="0" dock_id="1" table="4,18:mainall_series_as_json"/><table title="games" custom_title="0" dock_id="2" table="4,5:maingames"/><dock_state state="000000ff00000000fd00000001000000020000043c000002b6fc0100000001fc000000000000043c0000011e00fffffffa000000010100000002fb000000160064006f0063006b00420072006f00770073006500310100000000ffffffff0000011e00fffffffb000000160064006f0063006b00420072006f00770073006500320100000000ffffffff0000011b00ffffff0000043c0000000000000004000000040000000800000008fc00000000"/><default_encoding codec=""/><browse_table_settings><table schema="main" name="all_series_as_json" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="156"/><column index="2" value="429"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="backlog" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="31"/><column index="2" value="300"/><column index="3" value="58"/><column index="4" value="257"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="games" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort><column index="0" mode="0"/></sort><column_widths><column index="1" value="31"/><column index="2" value="93"/><column index="3" value="273"/><column index="4" value="300"/><column index="5" value="85"/><column index="6" value="70"/><column index="7" value="58"/><column index="8" value="70"/><column index="9" value="41"/></column_widths><filter_values><column index="3" value="prince "/></filter_values><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="games_available_time" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="40"/><column index="2" value="53"/><column index="3" value="53"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="games_genres" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="41"/><column index="2" value="42"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="games_schedules" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="31"/><column index="2" value="85"/><column index="3" value="85"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table></browse_table_settings></tab_browse><tab_sql><sql name="SQL 1*">SELECT * FROM dlcs_as_json</sql><current_tab id="0"/></tab_sql></sqlb_project>
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ There is a [database](GamesPassionFR.db) for managing video games, their genres,
| `availableAt`| Date (and time) when the game becomes available. | Optional | `2023-08-01 10:00:00`|
| `endAt` | Date (and time) when the game is no longer available.| Optional | `2023-12-31 23:59:59`|

## `games_dlcs` Table

| Field | Description | Required/Optional | Example |
|---------|-------------------------------------------------------|-------------------|----------------------|
| `game` | Foreign key referencing the `games` table. | Required | `2` |
| `dlc` | Foreign key referencing the `games` table. | Required | `21` |
| `order` | The order of the dlc, in the game. | Optional | `1` |

### `genres` Table

| Field | Description | Required/Optional | Example |
Expand Down Expand Up @@ -124,4 +132,4 @@ There is a [database](GamesPassionFR.db) for managing video games, their genres,
| `platform` | Foreign key referencing the `platforms` table. | Required | `2` |
| `duration` | The duration of the test or video content. | Optional | `00:30:00` |

[YoutubeChannel]: https://www.youtube.com/channel/UCG0N7IV-C43AM9psxslejCQ
[YoutubeChannel]: https://www.youtube.com/channel/UCG0N7IV-C43AM9psxslejCQ
48 changes: 48 additions & 0 deletions automatedTasks.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,51 @@ async function manageSerieInDatabase(db, payload) {
return updateSerieItems();
}

/**
* Manage dlcs in database
* @param {import('better-sqlite3').Database} db - The database instance
* @param {Object} payload - The details
* @param {string} payload.gameID - The ID of the game (videoId or playlistId)
* @param {string} payload.dlcs_textarea - The dlc of this game (as textarea)
*
*/
async function manageDlcsInDatabase(db, payload) {

// Fetch games ID
const dlcs = payload.dlcs_textarea
.split("\n")
.map(s => s.trim())
.filter(s => s.length > 0);

// Statements
const fetchGameByIdStmt = db.prepare('SELECT id FROM games WHERE videoId = @id OR playlistId = @id');
const deleteGameDLCsStmt = db.prepare('DELETE FROM games_dlcs WHERE game = ?');
const insertDLCToGameStmt = db.prepare('INSERT INTO games_dlcs (game, dlc, `order`) VALUES (?, ?, ?)');

// Execution time
const gameID = fetchGameByIdStmt.pluck().get({ id: payload.gameID });
await deleteGameDLCsStmt.run(gameID);

const updateDLCSItems = db.transaction(() => {

let idx = 1;
for(const gameIdentifier of dlcs) {

// Fetch game id
const dlcID = fetchGameByIdStmt.pluck().get({ id: gameIdentifier });

// Insert the dlc's order in the game
insertDLCToGameStmt.run(gameID, dlcID, idx);

// Next iteration
idx++;
}

});

return updateDLCSItems();
}

switch(taskType) {
case "ADD_GAME":
await addGameToDatabase(db, taskPayload);
Expand All @@ -453,6 +498,9 @@ switch(taskType) {
case "MANAGE_SERIE":
await manageSerieInDatabase(db, taskPayload);
break;
case "MANAGE_DLCS":
await manageDlcsInDatabase(db, taskPayload);
break;
default:
console.log(`Bip bip - Nothing was done as unexpected task`)
}
17 changes: 17 additions & 0 deletions generateJsonFiles.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const FILES = {
"PLANNING": "src/app/api/planning/planning.json",
"STATS": "src/app/api/stats/stats.json",
"PAST_GAMES": "src/app/api/planning/past-planning.json",
"DLCS": "src/app/api/dlcs/dlcs.json",
}

const db = new Database(databasePath, {
Expand Down Expand Up @@ -223,6 +224,21 @@ async function extractAndSavePastGames(db) {
console.log(`${FILES.PAST_GAMES} successfully written`);
}

/**
* Extracts dlcs from the database and saves them to a file.
* @param {import('better-sqlite3').Database} db - The database instance
*/
async function extractAndSaveDLCS(db) {
const extractDLCSStmt = db.prepare("SELECT * FROM dlcs_as_json");
const dlcs = extractDLCSStmt.all();
await writeFile(
FILES.DLCS,
stringifyJSON(dlcs),
"utf-8"
);
console.log(`${FILES.DLCS} successfully written`);
}

// Operations time
await extractAndSavePlatforms(db)
await extractAndSaveGenres(db)
Expand All @@ -233,3 +249,4 @@ await extractAndSaveSeries(db)
await extractAndSaveTests(db)
await extractAndSaveStats(db)
await extractAndSavePastGames(db);
await extractAndSaveDLCS(db);
3 changes: 2 additions & 1 deletion messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"sortButtonLabel": "Sorts",
"tabs": {
"grid": "Games",
"list": "Series"
"list": "Series",
"dlc": "DLCS"
},
"sortForm": {
"firstSort": "Sort by",
Expand Down
3 changes: 2 additions & 1 deletion messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"sortButtonLabel": "Tris",
"tabs": {
"grid": "Jeux",
"list": "Séries"
"list": "Séries",
"dlc": "DLCS"
},
"sortLabels": {
"name": "Nom",
Expand Down
14 changes: 8 additions & 6 deletions src/app/[locale]/games/_client/GalleryMode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,20 @@ import Tab from '@mui/material/Tab';
// Icons
import AppsIcon from '@mui/icons-material/Apps';
import ListIcon from '@mui/icons-material/List';
import ExtensionIcon from '@mui/icons-material/Extension';

// Views
type ViewType = "GRID" | "LIST";
const MODES = ["GRID", "LIST", "DLC"] as const;
type ViewType = typeof MODES[number];

function ModeSelector() {

const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
const t = useTranslations("gamesLibrary.tabs");
const viewMode : ViewType = searchParams.get("mode") === "LIST" ? "LIST" : "GRID";
const userMode = searchParams.get("mode");
const viewMode : ViewType = MODES.includes(userMode as any) ? userMode as ViewType : "GRID";

// Get a new searchParams string by merging the current
// searchParams with a provided key/value pair
Expand All @@ -42,13 +45,12 @@ function ModeSelector() {
router.push(pathname + '?' + createQueryString('mode', newValue))
};

const labelGenerator = (value : 'GRID' | 'LIST') => value === "GRID" ? t("grid") : t("list");

return (
<div style={{display: "flex", justifyContent: "center"}}>
<Tabs value={viewMode} onChange={handleChange} aria-label="icon label tabs" centered>
<Tab role="tab" id="tab-GRID" aria-selected={viewMode === "GRID"} icon={<AppsIcon />} label={labelGenerator("GRID")} value="GRID" />
<Tab role="tab" id="tab-LIST" aria-selected={viewMode === "LIST"} icon={<ListIcon />} label={labelGenerator("LIST")} value="LIST" />
<Tab role="tab" id="tab-GRID" aria-selected={viewMode === "GRID"} icon={<AppsIcon />} label={t("grid")} value="GRID" />
<Tab role="tab" id="tab-LIST" aria-selected={viewMode === "LIST"} icon={<ListIcon />} label={t("list")} value="LIST" />
<Tab role="tab" id="tab-LIST" aria-selected={viewMode === "LIST"} icon={<ExtensionIcon />} label={t("dlc")} value="DLC" />
</Tabs>
</div>
)
Expand Down
92 changes: 92 additions & 0 deletions src/app/[locale]/games/_client/GamesGalleryDLC.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"use client";

// Next js
import dynamic from 'next/dynamic'

// Hooks
import { useGetDLCsQuery } from "@/redux/services/dlcsAPI";

// MUI component
import Grid from "@mui/material/Grid";
import Skeleton from '@mui/material/Skeleton';
import Accordion from '@mui/material/Accordion';
import AccordionSummary from '@mui/material/AccordionSummary';
import Typography from '@mui/material/Typography';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';

// Custom
const CardEntry = dynamic(() => import('@/components/GamesView/CardEntry'), { ssr: false });
const AccordionDetails = dynamic(() => import('@mui/material/AccordionDetails'), { ssr: false });

// The gallery component
function GamesGalleryList() {

const { data, error, isLoading } = useGetDLCsQuery();

if (error) {
return <>Something bad happened</>;
}

if (isLoading) {
return (
<div>
{Array.from({ length: 5 }).map((_, index) => (
<div key={index} style={{ marginBottom: '15px' }}>
<Skeleton variant="rectangular" width="100%" height={50} />
</div>
))}
</div>
);
}

if (!data) {
return null;
}

return (
<div>
{
data.map(game =>
<Accordion key={game.name}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls={"panel-content" + game.name}
id={"panel-header" + game.name}
>
<Typography>{game.name}</Typography>
</AccordionSummary>
<AccordionDetails>
<Grid
container
spacing={1}
style={
{
rowGap: "15px"
}
}
>
{
game
.items
.map(dlc =>
<Grid
key={dlc.id}
item
xs={6}
md={4}
lg={1.5}
>
<CardEntry game={dlc}/>
</Grid>
)
}
</Grid>
</AccordionDetails>
</Accordion>
)
}
</div>
)
}

export default GamesGalleryList;
Loading

0 comments on commit b449a3c

Please sign in to comment.