Skip to content
This repository was archived by the owner on Dec 22, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions common/randomId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const randomId = (n = 18) => {
const bytes = String.fromCharCode(...Array.from(crypto.getRandomValues(new Uint8Array(n))));
return window.btoa(bytes);
};
79 changes: 79 additions & 0 deletions common/sortUtils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable valid-jsdoc */
import {isNumber} from 'common/checkVariableTypeUtil';

export enum SortingOrder{
Expand All @@ -22,4 +23,82 @@ export const sortTwoUnknownValues = (valueA: unknown, valueB: unknown,
}
};

type Comparator<Item> = (a: Item, b: Item) => number;

const Ordering = {
/** They are equals. */
Equal: 0,
/** The first value is sorted after the second. */
First: +1,
/** The second value is sorted after the first. */
Second: -1,
} as const;

const isNully = (x: unknown): x is null | undefined => {
return x === null || x === undefined;
};

/**
* @param keySelector
* A mapping function to extract key from an array element.
* Note that this function can return null or undefined,
* but these values are treated as greater than other values.
* @param comparatorFn
* This comparator functions are applied in order, starting with the first,
* and if the previous returns 0, the next is applied.
* When all functions s return 0, it means the two elements are equal.
* @return a comparator function.
*/
export const buildComparator = <Item, Key>(
keySelector: (item: Item) => (Key | null | undefined),
...comparatorFn: Comparator<Key>[]
): Comparator<Item> => {
return (firstItem, secondItem) => {
if (firstItem === secondItem) return Ordering.Equal;
const first = keySelector(firstItem);
const second = keySelector(secondItem);
if (first === second) return Ordering.Equal;

if (isNully(first) || isNully(second)) {
return (first === second) ? Ordering.Equal :
isNully(first) ? Ordering.First :
isNully(second) ? Ordering.Second :
'unreachable' as never;
}

for (const comparator of comparatorFn) {
const ordering = comparator(first, second);
if (ordering != Ordering.Equal) return ordering;
}

return Ordering.Equal;
};
};

const numberAscending: Comparator<number> = (a, b) => a - b;
const numberDescending: Comparator<number> = (a, b) => b - a;
const stringAscending: Comparator<string> = (a, b) => a.localeCompare(b);
const stringDescending: Comparator<string> = (a, b) => b.localeCompare(a);
const numericStringAscending = buildComparator(parseFloat, numberAscending);
const numericStringDescending = buildComparator(parseFloat, numberDescending);
export const Comparators = {
numberAscending, numberDescending,
stringAscending, stringDescending,
numericStringAscending, numericStringDescending,
} as const;

export const buildArrayIndexComparator = <Item>(
array: readonly Item[],
order: SortingOrder = SortingOrder.Ascending,
) => {
const comparator = order === SortingOrder.Ascending ?
Comparators.numberAscending :
Comparators.numberDescending;
return buildComparator<Item, number>(
(item) => {
const index = array.indexOf(item);
return index >= 0 ? index : null;
},
comparator,
);
};
22 changes: 14 additions & 8 deletions components/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import {Equipment, EquipmentCompositionType} from 'model/Equipment';
import {Campaign} from 'model/Campaign';
import RecommendedCampaigns from 'components/calculationResult/RecommendedCampaigns';
import CalculationInputCard from 'components/calculationInput/CalculationInputCard';
import {CampaignsById, EquipmentsById} from 'components/calculationInput/PiecesCalculationCommonTypes';
import {
CampaignsById, EquipmentsById,
} from 'components/calculationInput/PiecesCalculationCommonTypes';
import useSWR from 'swr';
import RecommendationsSummary from 'components/calculationResult/RecommendationsSummary';
import IgnoredCampaigns from 'components/calculationResult/IgnoredCampaigns';
Expand All @@ -23,7 +25,10 @@ import {
hashTierAndCategoryKey,
} from 'components/calculationInput/equipments/EquipmentsInput';
import {PieceState} from 'components/calculationInput/equipments/inventory/PiecesInventory';
import {calculatePiecesState} from 'components/calculationInput/equipments/inventory/piecesStateCalculator';
import {calculatePiecesState}
from 'components/calculationInput/equipments/inventory/piecesStateCalculator';
import {AddToInventoryDialogContextProvider}
from './calculationInput/equipments/inventory/AddToInventoryDialog';

const Home: NextPage = observer((props) => {
const store = useStore();
Expand Down Expand Up @@ -120,14 +125,15 @@ const Home: NextPage = observer((props) => {
equipmentsById={equipmentsById}
equipmentsRequirementStore={store.equipmentsRequirementStore}
normalMissionItemDropRatio={store.gameInfoStore.normalMissionItemDropRatio}
onCloseInEfficacyDialog={handleCloseInEfficacyDialog}/>
onCloseInEfficacyDialog={handleCloseInEfficacyDialog}
piecesState={piecesState} />
<IgnoredCampaigns
solution={solution}
allCampaigns={campaigns}
allRequiredPieceIds={store.equipmentsRequirementStore.getAllRequiredPieceIds()}
equipmentsById={equipmentsById}
gameServer={store.gameInfoStore.gameServer}
/>
piecesState={piecesState} />
</React.Fragment>;
};

Expand All @@ -149,10 +155,10 @@ const Home: NextPage = observer((props) => {
onSetSolution={onSetSolution}
/>

{
store.equipmentsRequirementStore.resultMode === ResultMode.LinearProgram ?
buildLinearProgrammingSolution() : buildListStageOnlyResult()
}
<AddToInventoryDialogContextProvider equipById={equipmentsById} piecesState={piecesState}>
{store.equipmentsRequirementStore.resultMode === ResultMode.LinearProgram ?
buildLinearProgrammingSolution() : buildListStageOnlyResult()}
</AddToInventoryDialogContextProvider>
</>;
});

Expand Down
20 changes: 14 additions & 6 deletions components/calculationInput/common/PositiveIntegerOnlyInput.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
import {TextField} from '@mui/material';
import {TextField, TextFieldProps} from '@mui/material';
import {Controller, FieldValues, Path} from 'react-hook-form';
import React from 'react';
import {useTranslation} from 'next-i18next';
import {Control} from 'react-hook-form/dist/types/form';

interface PositiveIntegerOnlyInputProps<T extends FieldValues> {
interface PositiveIntegerOnlyInputProps<T extends FieldValues>
extends Omit<TextFieldProps,
| 'inputProps' | 'variant' | 'error' | 'helperText' | 'label'
| 'onChange' | 'onBlur' | 'value' | 'name' | 'ref'>
{
name: Path<T>;
control: Control<T>;
showError: boolean;
helperText: string;
min?: number;
inputLabel?: string;
required?: boolean;
}

const PositiveIntegerOnlyInput = function<T>({
const PositiveIntegerOnlyInput = function<T extends FieldValues>({
name, control, showError, helperText,
min = 1, inputLabel,
min = 1, inputLabel, required = true,
...others
}: PositiveIntegerOnlyInputProps<T>) {
const {t} = useTranslation('home');
return <Controller
name={name}
control={control}
rules={{
required: {
value: true,
value: required,
message: t('addPieceDialog.required'),
},
pattern: {
Expand All @@ -39,9 +45,11 @@ const PositiveIntegerOnlyInput = function<T>({
message: t('addPieceDialog.maximumIs', {max: 999}),
},
}}
render={({field}) => (
render={({field: {ref: fieldRef, ...field}}) => (
<TextField
{...others}
{...field}
inputRef={fieldRef}
inputProps={{pattern: '\\d*'}}
variant="outlined"
error={showError}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@use 'components/calculationInput/common/common-calculation-input-layout';

.container {
display: grid;
justify-content: center;
grid-template-columns: repeat(auto-fill, 350px);
grid-gap: 10px;
}

.xs {
grid-template-columns: repeat(auto-fill, 300px);
}

.equipmentInputContainer {
display: flex;
margin: 10px;
align-items: center;
}

.equipmentCard {
@include common-calculation-input-layout.items-list;
}
Loading