diff --git a/package.json b/package.json
index 0cc3fb837..894cf2952 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,7 @@
"@sentry/node": "^6.5.1",
"accept-language-parser": "^1.5.0",
"autosuggest-highlight": "^3.1.1",
+ "date-fns": "^3.6.0",
"isomorphic-unfetch": "^3.1.0",
"isomorphic-xml2js": "^0.1.3",
"jest": "^27.0.4",
diff --git a/src/components/FeaturePanel/Climbing/RouteList/MyTicks.tsx b/src/components/FeaturePanel/Climbing/RouteList/MyTicks.tsx
index 6fc7d9db9..467f5395d 100644
--- a/src/components/FeaturePanel/Climbing/RouteList/MyTicks.tsx
+++ b/src/components/FeaturePanel/Climbing/RouteList/MyTicks.tsx
@@ -1,8 +1,14 @@
import React from 'react';
import styled from 'styled-components';
import DeleteIcon from '@material-ui/icons/Delete';
-import { Button } from '@material-ui/core';
-import { findTicks, onTickDelete } from '../../../../services/ticks';
+import { Button, FormControl, MenuItem, Select } from '@material-ui/core';
+import { format } from 'date-fns';
+import {
+ findTicks,
+ onTickDelete,
+ onTickUpdate,
+ tickStyles,
+} from '../../../../services/ticks';
import { PanelLabel } from '../PanelLabel';
const Container = styled.div`
@@ -10,29 +16,59 @@ const Container = styled.div`
`;
const Item = styled.div`
font-size: 12px;
+ display: flex;
+ gap: 8px;
+ align-items: center;
`;
+const Date = styled.div``;
export const MyTicks = ({ osmId }) => {
const ticks = findTicks(osmId);
if (ticks.length === 0) return null;
- const deleteAscent = (index) => {
+ const deleteTick = (index) => {
onTickDelete({ osmId, index });
};
+ const onTickStyleChange = (event, index) => {
+ // @TODO tickId vs osmId
+ onTickUpdate({
+ osmId,
+ index,
+ updatedObject: { style: event.target.value },
+ });
+ };
+
return (
Ticks:
- {ticks.map((ascent, index) => (
- -
- {ascent.date}
-
- ))}
+ {ticks.map((tick, index) => {
+ const date = format(tick.date, 'd.M.yy');
+ return (
+ -
+ {date}
+
+
+
+
+
+
+ );
+ })}
);
};
diff --git a/src/components/FeaturePanel/Climbing/RouteList/RouteListDndContent.tsx b/src/components/FeaturePanel/Climbing/RouteList/RouteListDndContent.tsx
index 1959b3f92..ff54d48af 100644
--- a/src/components/FeaturePanel/Climbing/RouteList/RouteListDndContent.tsx
+++ b/src/components/FeaturePanel/Climbing/RouteList/RouteListDndContent.tsx
@@ -3,7 +3,6 @@ import React, { useEffect, useState } from 'react';
import DragIndicatorIcon from '@material-ui/icons/DragIndicator';
import { useClimbingContext } from '../contexts/ClimbingContext';
import { RenderListRow } from './RouteListRow';
-import { isTicked } from '../../../../services/ticks';
type Item = {
id: number;
@@ -26,7 +25,7 @@ const MaxWidthContainer = styled.div`
const RowWithDragHandler = styled.div<{
isDraggedOver: boolean;
- isTicked: boolean;
+ isSelected: boolean;
}>`
cursor: pointer;
display: flex;
@@ -34,12 +33,8 @@ const RowWithDragHandler = styled.div<{
-webkit-tap-highlight-color: rgba(255, 255, 255, 0.1);
/* background-color: ${({ isSelected }) =>
isSelected ? '#ccc' : 'transparent'}; */
- background: ${({ isSelected, theme, isTickeded }) =>
- isSelected
- ? theme.palette.action.selected
- : isTickeded
- ? theme.palette.climbing.tick
- : 'transparent'};
+ background: ${({ isSelected, theme }) =>
+ isSelected ? theme.palette.action.selected : 'transparent'};
position: relative;
font-size: 16px;
border-top: dotted 1px ${({ theme }) => theme.palette.divider};
@@ -215,9 +210,7 @@ export const RouteListDndContent = ({ isEditable }) => {
{items.map((item, index) => {
- const osmId = item.route.feature?.osmMeta.id ?? null;
const isSelected = isRouteSelected(index);
-
return (
{draggedItem?.id > index && (
@@ -230,7 +223,6 @@ export const RouteListDndContent = ({ isEditable }) => {
onDragOver={(e) => handleDragOver(e, index)}
onDragEnd={handleDragEnd}
isSelected={isSelected}
- isTickeded={isTicked(osmId)}
onClick={() => {
onRowClick(index);
}}
diff --git a/src/components/FeaturePanel/Climbing/RouteList/RouteListRow.tsx b/src/components/FeaturePanel/Climbing/RouteList/RouteListRow.tsx
index 0d615daa8..1537ed0bf 100644
--- a/src/components/FeaturePanel/Climbing/RouteList/RouteListRow.tsx
+++ b/src/components/FeaturePanel/Climbing/RouteList/RouteListRow.tsx
@@ -1,18 +1,19 @@
/* eslint-disable react/jsx-props-no-spreading */
import React, { useEffect, useRef, useState } from 'react';
import styled from 'styled-components';
-import { IconButton, TextField } from '@material-ui/core';
+import { IconButton, TextField, Tooltip } from '@material-ui/core';
import { debounce } from 'lodash';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+import CheckIcon from '@material-ui/icons/Check';
import { ClimbingRoute } from '../types';
import { useClimbingContext } from '../contexts/ClimbingContext';
import { emptyRoute } from '../utils/emptyRoute';
import { RouteNumber } from '../RouteNumber';
-
import { toggleElementInArray } from '../utils/array';
import { ExpandedRow } from './ExpandedRow';
import { RouteDifficultyBadge } from '../RouteDifficultyBadge';
import { getShortId } from '../../../../services/helpers';
+import { isTicked } from '../../../../services/ticks';
const DEBOUNCE_TIME = 1000;
const Container = styled.div`
@@ -27,6 +28,9 @@ const Cell = styled.div<{ width: number; align: 'center' | 'left' | 'right' }>`
`;
const NameCell = styled(Cell)`
flex: 1;
+ display: flex;
+ gap: 8px;
+ justify-content: space-between;
`;
const DifficultyCell = styled(Cell)`
margin-right: 8px;
@@ -104,7 +108,7 @@ export const RenderListRow = ({
setTempRoute({ ...tempRoute, [propName]: e.target.value });
debouncedValueChange(e, propName);
};
-
+ const ticked = isTicked(osmId);
const isExpanded = routesExpanded.indexOf(index) > -1;
const isReadOnly =
@@ -149,6 +153,11 @@ export const RenderListRow = ({
fullWidth
/>
)}
+ {ticked && (
+
+
+
+ )}
>;
+
+export type TickStyle =
+ | 'OS'
+ | 'FL'
+ | 'RP'
+ | 'PP'
+ | 'RK'
+ | 'AF'
+ | 'TR'
+ | 'FS'
+ | null;
+export type Tick = {
+ osmId: string;
+ style: TickStyle;
+ date: string;
+};
diff --git a/src/services/ticks.ts b/src/services/ticks.ts
index 996cb5eef..411f5b2b0 100644
--- a/src/services/ticks.ts
+++ b/src/services/ticks.ts
@@ -1,8 +1,52 @@
-import { JSONValue } from './types';
+import { Tick, TickStyle } from '../components/FeaturePanel/Climbing/types';
+import { updateElementOnIndex } from '../components/FeaturePanel/Climbing/utils/array';
const KEY = 'ticks';
-export const getLocalStorageItem = (key: string) => {
+export const tickStyles: Array<{
+ key: TickStyle;
+ name: string;
+ description?: string;
+}> = [
+ {
+ key: null,
+ name: 'Not selected',
+ },
+ {
+ key: 'OS',
+ name: 'On sight',
+ },
+ {
+ key: 'FL',
+ name: 'Flash',
+ },
+ {
+ key: 'RP',
+ name: 'Red point',
+ },
+ {
+ key: 'PP',
+ name: 'Pink point',
+ },
+ {
+ key: 'RK',
+ name: 'Red cross',
+ },
+ {
+ key: 'AF',
+ name: 'All free',
+ },
+ {
+ key: 'TR',
+ name: 'Top rope',
+ },
+ {
+ key: 'FS',
+ name: 'Free solo',
+ },
+];
+
+export const getLocalStorageItem = (key: string): Array => {
if (typeof window === 'undefined') return [];
const raw = window?.localStorage.getItem(key);
if (raw) {
@@ -17,7 +61,7 @@ export const getLocalStorageItem = (key: string) => {
return [];
};
-export const setLocalStorageItem = (key: string, value: JSONValue) => {
+export const setLocalStorageItem = (key: string, value: Array) => {
if (typeof window === 'undefined') return;
window?.localStorage.setItem(key, JSON.stringify(value));
};
@@ -27,13 +71,32 @@ export const onTickAdd = ({ osmId }) => {
const ticks = getLocalStorageItem(KEY);
setLocalStorageItem(KEY, [
...ticks,
- { id: osmId, date: new Date().toISOString() },
+ { osmId, date: new Date().toISOString(), style: null },
]);
};
-export const findTicks = (osmId: string) => {
+export const findTicks = (osmId: string): Array => {
const ticks = getLocalStorageItem(KEY);
- return ticks?.filter((tick) => osmId === tick.id) ?? null;
+
+ return ticks?.filter((tick) => osmId === tick.osmId) ?? null;
+};
+
+export const onTickUpdate = ({
+ osmId,
+ index,
+ updatedObject,
+}: {
+ osmId: string;
+ index: number;
+ updatedObject: Partial;
+}) => {
+ const routeTicks = findTicks(osmId);
+ const updatedArray = updateElementOnIndex(
+ routeTicks,
+ index,
+ (item) => ({ ...item, ...updatedObject }),
+ );
+ setLocalStorageItem(KEY, updatedArray);
};
export const onTickDelete = ({
@@ -47,7 +110,7 @@ export const onTickDelete = ({
const newArray = ticks.reduce(
(acc, tick) => {
- if (osmId === tick.id) {
+ if (osmId === tick.osmId) {
const newIndex = acc.index + 1;
if (acc.index === index) {
return {
diff --git a/src/services/types.ts b/src/services/types.ts
index 8070b1a96..bb905349a 100644
--- a/src/services/types.ts
+++ b/src/services/types.ts
@@ -111,10 +111,3 @@ export interface Feature {
export type MessagesType = typeof Vocabulary;
export type TranslationId = keyof MessagesType;
-
-export type JSONValue =
- | string
- | number
- | boolean
- | { [x: string]: JSONValue }
- | Array;
diff --git a/yarn.lock b/yarn.lock
index fe710bfd4..af0fc7dab 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3128,6 +3128,11 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
+date-fns@^3.6.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf"
+ integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==
+
debug@4, debug@^4.0.1, debug@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"