JamTracks

includes JamTrack filter page
This commit is contained in:
Nuwan 2024-06-10 13:45:15 +05:30
parent 24aab60556
commit 176ba1febe
21 changed files with 11380 additions and 16794 deletions

27393
jam-ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,7 @@
"faker": "^5.5.3",
"fuse.js": "^6.4.3",
"google-maps-react": "^2.0.6",
"howler": "^2.2.4",
"i18next": "^21.3.3",
"is_js": "^0.9.0",
"leaflet": "^1.7.1",

View File

@ -16,6 +16,9 @@ const JKAffiliateLinks = () => {
const [affiliatePartnerId, setAffiliatePartnerId] = useState('xxxxx');
const [affiliate, setAffiliate] = useState(null);
const [jamTracks, setJamTracks] = useState([]);
const [showDropdown, setShowDropdown] = useState(false);
const [autoCompleteInputValue, setAutoCompleteInputValue] = useState('');
const [jamTracksNextPage, setJamTracksNextPage] = useState(null);
useEffect(() => {
fetchAffiliate();
@ -34,28 +37,39 @@ const JKAffiliateLinks = () => {
}
}
const handleOnSelect = jamTrack => {
console.log('onSelect', jamTrack);
fetchJamTracks(jamTrack);
};
const fetchJamTracks = async selected => {
const options = {
limit: 100
};
if (selected.type === 'artist') {
options.artist = selected.original_artist;
} else {
options.song = selected.name;
}
const fetchJamTracks = async options => {
try {
const resp = await getJamTracks(options);
const data = await resp.json();
console.log('data', data);
setJamTracks(data.jamtracks);
setJamTracksNextPage(data.next);
} catch (error) {}
};
const queryOptions = selected => {
const options = {
limit: 100
};
if (typeof selected === 'string') {
options.search = selected;
return options;
}
if (selected.type === 'artist') {
options.artist = selected.original_artist;
} else {
options.song = selected.name;
}
if (jamTracksNextPage !== null) {
options.next = jamTracksNextPage;
}
return options;
};
const homePageLink = `https://www.jamkazam.com?affiliate=${affiliatePartnerId}`;
const handleClickOnHomePageLink = () => {
if(affiliate){
@ -77,6 +91,19 @@ const JKAffiliateLinks = () => {
}
}
//autocomplete related code
const handleOnSelect = selected => {
console.log('onSelect', selected);
const params = queryOptions(selected);
fetchJamTracks(params);
};
const handleOnEnter = queryStr => {
console.log('onEnter', queryStr);
const params = queryOptions(queryStr);
fetchJamTracks(params);
}
return (
<Card style={{ width: greaterThan.sm ? '75%' : '100%'}} className="mx-auto affiliate-links">
<FalconCardHeader title={t('links.page_title')} titleClass="font-weight-semi-bold" />
@ -90,7 +117,14 @@ const JKAffiliateLinks = () => {
<div className="affiliate-links__subtitle">{t('links.jamtracks_pages_subtitle')}</div>
<p>{t('links.jamtracks_pages_paragraph')}</p>
<div className='mt-4'>
<JKJamTracksAutoComplete onSelect={handleOnSelect} />
<JKJamTracksAutoComplete
onSelect={handleOnSelect}
onEnter={handleOnEnter}
showDropdown={showDropdown}
setShowDropdown={setShowDropdown}
inputValue={autoCompleteInputValue}
setInputValue={setAutoCompleteInputValue}
/>
</div>
{jamTracks &&
jamTracks.map(jamTrack => {

View File

@ -0,0 +1,9 @@
import React from 'react'
const JKShoppingCart = () => {
return (
<div>ShoppingCart</div>
)
}
export default JKShoppingCart

View File

@ -1,8 +1,9 @@
import React, { useState, useRef } from 'react';
import PropTypes from 'prop-types';
import { Tooltip } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
const JKTooltip = props => {
const JKTooltip = ({ title, color, icon, size, placement }) => {
const [tooltipOpen, setTooltipOpen] = useState(false);
const iconRef = useRef();
const toggle = () => setTooltipOpen(!tooltipOpen);
@ -11,24 +12,30 @@ const JKTooltip = props => {
e.preventDefault();
}
return (
<a href="#!" onClick={handleClick}>
<img
alt="help"
ref={iconRef}
src={require('../../assets/img/icons/question_icon.svg')}
height="12"
width="12"
style={{ opacity: '.7' }}
/>
<Tooltip placement="auto" isOpen={tooltipOpen} target={iconRef} toggle={toggle}>
{props.title}
<a href="#!" onClick={handleClick} style={{ color: color}}>
<span ref={iconRef}>
<FontAwesomeIcon icon={icon} size={size} />
</span>
<Tooltip placement={placement} isOpen={tooltipOpen} target={iconRef} toggle={toggle}>
{title}
</Tooltip>
</a>
);
};
JKTooltip.propTypes = {
title: PropTypes.string.isRequired
title: PropTypes.string.isRequired,
color: PropTypes.string,
icon: PropTypes.string,
size: PropTypes.string,
placement: PropTypes.string
}
JKTooltip.defaultProps = {
color: '#999',
icon: 'question-circle',
size: '1x',
placement: 'auto'
}
export default JKTooltip;

View File

@ -48,6 +48,10 @@ import JKAffiliateSignups from '../affiliate/JKAffiliateSignups';
import JKAffiliateEarnings from '../affiliate/JKAffiliateEarnings';
import JKAffiliateAgreement from '../affiliate/JKAffiliateAgreement';
import JKJamTracksFilter from '../jamtracks/JKJamTracksFilter';
import JKShoppingCart from '../cart/JKShoppingCart';
//import loadable from '@loadable/component';
//const DashboardRoutes = loadable(() => import('../../layouts/JKDashboardRoutes'));
@ -282,6 +286,8 @@ function JKDashboardMain() {
<PrivateRoute path="/affiliate/signups" component={JKAffiliateSignups} />
<PrivateRoute path="/affiliate/earnings" component={JKAffiliateEarnings} />
<PrivateRoute path="/affiliate/agreement" component={JKAffiliateAgreement} />
<PrivateRoute path="/jamtracks/search" component={JKJamTracksFilter} />
<PrivateRoute path="/cart" component={JKShoppingCart} />
{/*Redirect*/}
<Redirect to="/errors/404" />
</Switch>

View File

@ -0,0 +1,59 @@
import React, { useState } from 'react';
import { Row, Col } from 'reactstrap';
const JKJamTrackArtists = ({ artists, showArtists, onSelect }) => {
const [expanded, setExpanded] = useState(false);
const handleClick = artist => {
onSelect(artist);
};
const toggleMoreArtists = e => {
e.preventDefault();
setExpanded(!expanded);
}
return (
showArtists && (
<>
<Row>
<Col>
<strong>Search Results: Artists</strong>
</Col>
</Row>
{artists.length > 0 ? (
<>
<Row className="mb-2">
<Col>
{artists.map((artist, index) => (
<a
href="#"
key={`${index}_${artist.original_artist}`}
onClick={() => handleClick(artist)}
className={index + 1 > 6 && !expanded ? 'd-none' : null}
>
{artist.original_artist}
</a>
))}
</Col>
</Row>
{artists.length > 6 && (
<Row>
<Col>
<a href="#" onClick={toggleMoreArtists}>
{expanded ? 'Show fewer artists' : 'Show all artists'}
</a>
</Col>
</Row>
)}
</>
) : (
<Row className="mb-2">
<Col>No matching artists</Col>
</Row>
)}
</>
)
);
};
export default JKJamTrackArtists;

View File

@ -0,0 +1,36 @@
import React, { Fragment, useState } from 'react';
import { Row, Col, Container } from 'reactstrap';
import JKJamTrackTrack from './JKJamTrackTrack';
const JKJamTrackPreview = ({ jamTrack }) => {
const [expanded, setExpanded] = useState(false);
const toggleMoreTracks = (e) => {
e.preventDefault();
setExpanded(!expanded);
};
return (
<Container>
<Row>
{jamTrack.tracks.map((track, index) => (
<Fragment key={track.id} >
<Col className={ index + 1 > 6 && !expanded ? 'd-none' : null}>
<JKJamTrackTrack track={track} />
</Col>
{(index + 1) % 3 === 0 && <div className="w-100" />}
</Fragment>
))}
</Row>
{jamTrack.tracks.length > 6 && (
<Row>
<Col>
<a href="#" onClick={toggleMoreTracks}>{expanded ? 'Show fewer tracks' : 'Show all tracks'}</a>
</Col>
</Row>
)}
</Container>
);
};
export default JKJamTrackPreview;

View File

@ -0,0 +1,56 @@
import React from 'react';
import { Button } from 'reactstrap';
import PropTypes from 'prop-types';
import { addJamtrackToShoppingCart } from '../../helpers/rest';
import { useHistory } from 'react-router-dom';
import { useAuth } from '../../context/UserAuth';
const JKJamTrackPurchaseButton = ({ jamTrack }) => {
const history = useHistory();
const { currentUser } = useAuth();
console.log('currentUser', currentUser);
const addToCart = () => {
console.log('Add to Cart');
const options = {
id: jamTrack.id,
variant: 'full'
};
addJamtrackToShoppingCart(options)
.then(response => {
console.log('Add to Cart Response', response);
history.push('/cart');
})
.catch(error => {
console.log('Add to Cart Error', error);
});
};
return (
<>
{jamTrack.purchaed ? (
<Button color="light" size="sm" className="mr-1">
Purchased
</Button>
) : jamTrack.is_free && currentUser && currentUser.show_free_jamtrack ? (
<Button color="primary" onClick={addToCart} size="sm" className="mr-1">
Get It Free!
</Button>
) : jamTrack.added_cart ? (
<>
<Button color="light" size="sm" className="mr-1">
Already in Cart
</Button>
</>
) : (
<>
<div className="fs-1">$ {jamTrack.download_price}</div>
<Button color="primary" size="sm" className="mr-1" onClick={addToCart}>
Add to Cart
</Button>
</>
)}
</>
);
};
export default JKJamTrackPurchaseButton;

View File

@ -0,0 +1,132 @@
import React, { useState, useEffect } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import JKInstrumentIcon from '../profile/JKInstrumentIcon';
import { Howl } from 'howler';
import { useJamTrackPreview } from '../../context/JamTrackPreviewContext';
import { Spinner } from 'reactstrap';
const JKJamTrackTrack = ({ track }) => {
console.log('debug JKTrackPlayPause track');
const [isPlaying, setIsPlaying] = useState(false);
const [isLoaded, setIsLoaded] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [trackInfo, setTrackInfo] = useState(null);
const [trackSound, setTrackSound] = useState(null);
const { currentPlayTrackId, setCurrentPlayTrackId } = useJamTrackPreview();
useEffect(() => {
if (!track) {
return;
}
const info = {
id: track.id,
instrumentId: 'other',
instrumentDescription: 'Master Mix',
part: ''
};
if (track.instrument) {
info['instrumentId'] = track.instrument.id;
info['instrumentDescription'] = track.instrument.description;
}
if (track.track_type === 'Track') {
if (track.part && track.part !== info['instrumentDescription']) {
info['part'] = `(${track.part})`;
}
} else {
}
let no_audio = true;
let urls = [];
if (track.preview_mp3_url) {
urls.push(track.preview_mp3_url);
if (track.preview_ogg_url != null) {
urls.push(track.preview_ogg_url);
}
no_audio = false;
}
info['urls'] = urls;
info['no_audio'] = no_audio;
setTrackInfo(info);
}, [track]);
useEffect(() => {
if (!trackInfo || !trackSound) {
return;
}
if (trackInfo.id !== currentPlayTrackId) {
trackSound.pause();
setIsPlaying(false);
}
}, [currentPlayTrackId]);
const togglePlay = () => {
if (trackSound) {
if (isPlaying) {
trackSound.pause();
} else {
trackSound.play();
setCurrentPlayTrackId(trackInfo.id);
}
setIsPlaying(!isPlaying);
} else {
if (!trackInfo || Object.keys(trackInfo).length === 0 || trackInfo.no_audio || trackInfo.urls.length === 0) {
return;
}
setIsLoading(true);
const sound = new Howl({
src: trackInfo.urls,
html5: true,
format: ['mp3', 'ogg'],
preload: true,
autoplay: false,
loop: false,
volume: 1.0,
onend: () => {
setIsPlaying(false);
},
onload: () => {
setIsLoaded(true);
setIsLoading(false);
sound.play();
setIsPlaying(true);
setTrackSound(sound);
setCurrentPlayTrackId(trackInfo.id);
},
onloaderror: (id, error) => {
console.error('onloaderror', error);
setIsLoading(false);
}
});
}
};
return (
<div className="mb-1 d-flex align-items-center">
<span className="mr-1">
<FontAwesomeIcon icon={isPlaying ? 'pause-circle' : 'play-circle'} size="2x" onClick={togglePlay} />
</span>
{trackInfo && (
<>
<span className="mr-1">
<JKInstrumentIcon instrumentId={trackInfo.instrumentId} instrumentName={trackInfo.instrumentDescription} />
</span>
<span>
{trackInfo.instrumentDescription} {trackInfo.part}
</span>
{isLoading && (
<span className="ml-1">
<Spinner color="primary" size="sm">
Loading...
</Spinner>
</span>
)}
</>
)}
</div>
);
};
export default JKJamTrackTrack;

View File

@ -3,20 +3,19 @@ import { Row, Col, FormGroup, Input, InputGroup, InputGroupText, ListGroup, List
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useTranslation } from 'react-i18next';
import { autocompleteJamTracks } from '../../helpers/rest';
import Loader from '../common/Loader';
const JKJamTracksAutoComplete = ({ onSelect }) => {
const JKJamTracksAutoComplete = ({ onSelect, onEnter, showDropdown, setShowDropdown, inputValue, setInputValue }) => {
const [artists, setArtists] = useState([]);
const [songs, setSongs] = useState([]);
const [showDropdown, setShowDropdown] = useState(false);
const [inputValue, setInputValue] = useState('');
const [songs, setSongs] = useState([]);
//const [showDropdown, setShowDropdown] = useState(false);
//const [inputValue, setInputValue] = useState('');
const [loading, setLoading] = useState(false);
const inputRef = useRef(null);
const { t } = useTranslation();
const MIN_FILTER_SIZE = 3;
const MIN_FETCH_LIMIT = 5;
const fetchJamTracks = useCallback(() => {
const fetchAutoCompleteResults = useCallback(() => {
// fetch tracks
setLoading(true);
autocompleteJamTracks(inputValue, MIN_FETCH_LIMIT)
@ -28,23 +27,24 @@ const JKJamTracksAutoComplete = ({ onSelect }) => {
const updatedSongs = data.songs.map(song => {
song.type = 'song';
return song;
})
});
setSongs(updatedSongs);
const updatedArtists = data.artists.map(artist => {
artist.type = 'artist';
return artist;
})
});
setArtists(updatedArtists);
setShowDropdown(true);
}).finally(() => {
})
.finally(() => {
setLoading(false);
});
}, [inputValue]);
useEffect(() => {
if (inputValue.length >= MIN_FILTER_SIZE) {
fetchJamTracks();
}else{
if (inputValue && inputValue.length >= MIN_FILTER_SIZE) {
fetchAutoCompleteResults();
} else {
setShowDropdown(false);
}
}, [inputValue]);
@ -54,17 +54,12 @@ const JKJamTracksAutoComplete = ({ onSelect }) => {
setInputValue(val);
};
// const handleOnKeyDown = event => {
// if (event.key !== undefined) {
// if (event.key === 'Enter') {
// const first = tracks[0];
// onSelect(first);
// handleAfterSelect();
// } else if (event.key === 'ArrowDown') {
// console.log(event.target);
// }
// }
// };
const handleOnKeyDown = event => {
if (event.key && event.key === 'Enter' && inputValue.length > 0) {
setShowDropdown(false);
onEnter(inputValue);
}
};
const handleOnClick = track => {
onSelect(track);
@ -89,23 +84,26 @@ const JKJamTracksAutoComplete = ({ onSelect }) => {
<Row className="mb-2">
<Col md={8}>
<FormGroup className="mb-3">
<InputGroup>
<InputGroupText>
<FontAwesomeIcon icon="search" transform="shrink-4 down-1" />
</InputGroupText>
<div className="d-flex align-items-center">
<InputGroup>
<InputGroupText style={{ borderRadius: '0', borderRight: '0' }}>
{loading ? (
<span className="spinner-grow spinner-grow-sm" aria-hidden="true" />
) : (
<FontAwesomeIcon icon="search" transform="shrink-4 down-1" />
)}
</InputGroupText>
<Input
onChange={handleInputChange}
// onKeyDown={handleOnKeyDown}
value={inputValue}
innerRef={inputRef}
placeholder={t('new.invitations_filter_placeholder', { ns: 'sessions' })}
data-testid="autocomplete-text"
/>
<span>
{loading && <Loader />}
</span>
</InputGroup>
<Input
onChange={handleInputChange}
onKeyDown={handleOnKeyDown}
value={inputValue}
innerRef={inputRef}
placeholder={t('new.invitations_filter_placeholder', { ns: 'sessions' })}
data-testid="autocomplete-text"
/>
</InputGroup>
</div>
<ListGroup className={showDropdown ? 'd-block' : 'd-none'} data-testid="autocomplete-list">
{artists.map((track, index) => (
@ -125,7 +123,7 @@ const JKJamTracksAutoComplete = ({ onSelect }) => {
</div>
</ListGroupItem>
))}
{songs.map((track) => (
{songs.map(track => (
<ListGroupItem
key={track.id}
onMouseOver={highlight}
@ -140,7 +138,7 @@ const JKJamTracksAutoComplete = ({ onSelect }) => {
alignContent: 'flex-start'
}}
>
<div style={{ width: "50%"}}>{track.name}</div>
<div style={{ width: '50%' }}>{track.name}</div>
<div>{track.original_artist}</div>
</div>
</ListGroupItem>

View File

@ -0,0 +1,162 @@
import React, { useState, useEffect } from 'react';
import { Card, CardBody, Row, Col } from 'reactstrap';
import FalconCardHeader from '../common/FalconCardHeader';
import { useTranslation } from 'react-i18next';
import JKJamTracksAutoComplete from './JKJamTracksAutoComplete';
import { getJamTracks, getJamTrackArtists } from '../../helpers/rest';
import JKJamTrackArtists from './JKJamTrackArtists';
import JKJamTracksList from './JKJamTracksList';
const JKJamTracksFilter = () => {
const { t } = useTranslation('jamtracks');
const [jamTracks, setJamTracks] = useState([]);
const [artists, setArtists] = useState([]);
const [selected, setSelected] = useState(null);
const [searchTerm, setSearchTerm] = useState('');
const [showDropdown, setShowDropdown] = useState(false);
const [showArtists, setShowArtists] = useState(false);
const [jamTracksNextPage, setJamTracksNextPage] = useState(null);
const [autoCompleteInputValue, setAutoCompleteInputValue] = useState('');
useEffect(() => {
if (selected) {
setSearchTerm(selected.type === 'artist' ? selected.original_artist : selected.name);
}
}, [selected]);
const handleOnSelect = selected => {
setArtists([]);
setJamTracks([]);
setSearchTerm('');
setShowArtists(false);
setSelected(selected);
const params = queryOptions(selected);
console.log('handleOnSelect _params_', params);
fetchJamTracks(params);
};
const queryOptions = selected => {
const options = {
limit: 100
};
if (typeof selected === 'string') {
options.search = selected;
return options;
}
if (selected.type === 'artist') {
options.artist = selected.original_artist;
} else {
options.song = selected.name;
}
if (jamTracksNextPage !== null) {
options.next = jamTracksNextPage;
}
return options;
};
const handleOnEnter = queryStr => {
setArtists([]);
setJamTracks([]);
setSelected(null);
setSearchTerm(queryStr);
fetchArtists(queryStr);
const params = queryOptions(queryStr);
console.log('handleOnEnter _params_', params);
fetchJamTracks(params);
};
const handleOnSelectArtist = artist => {
const selectedOpt = {
type: 'artist',
original_artist: artist.original_artist
};
setShowDropdown(false);
setAutoCompleteInputValue('');
handleOnSelect(selectedOpt);
};
const fetchJamTracks = options => {
getJamTracks(options)
.then(resp => {
return resp.json();
})
.then(data => {
console.log('jamtracks', data);
setJamTracks(data.jamtracks);
setJamTracksNextPage(data.next);
})
.catch(error => {
console.error('error', error);
});
};
const fetchArtists = query => {
const options = {
limit: 100
};
options.artist_search = query;
getJamTrackArtists(options)
.then(resp => {
return resp.json();
})
.then(data => {
console.log('artists', data);
setArtists(data.artists);
setShowArtists(true);
})
.catch(error => {
console.error('error', error);
});
};
const handleOnNextJamTracksPage = () => {
const currentQuery = selected ? selected : searchTerm;
const params = queryOptions(currentQuery);
fetchJamTracks(params);
}
return (
<Card>
<FalconCardHeader title={t('search.page_title')} titleClass="font-weight-bold" />
<CardBody className="pt-3">
<Row>
<Col>
<JKJamTracksAutoComplete
onSelect={handleOnSelect}
onEnter={handleOnEnter}
showDropdown={showDropdown}
setShowDropdown={setShowDropdown}
inputValue={autoCompleteInputValue}
setInputValue={setAutoCompleteInputValue}
/>
</Col>
<Col className="text-right">
<span>
Download JamTracks catalog as a{' '}
<a href="https://s3.amazonaws.com/jamkazam-public/public/lists/all-jamkazam-jamtracks.pdf">
<strong>PDF file</strong>
</a>
</span>
</Col>
</Row>
<div className="mb-3">
<JKJamTrackArtists
artists={artists}
searchTerm={searchTerm}
onSelect={handleOnSelectArtist}
showArtists={showArtists}
/>
</div>
<JKJamTracksList selectedType={selected?.type} searchTerm={searchTerm} jamTracks={jamTracks} nextPage={jamTracksNextPage} onNextPage={handleOnNextJamTracksPage} />
</CardBody>
</Card>
);
};
export default JKJamTracksFilter;

View File

@ -0,0 +1,74 @@
import React from 'react';
import { Row, Col, Table, Button } from 'reactstrap';
import JKJamTrackPreview from './JKJamTrackPreview';
import JKJamTrackPurchaseButton from './JKJamTrackPurchaseButton';
import { JamTrackPreviewProvider } from '../../context/JamTrackPreviewContext';
const JKJamTracksList = ({ selectedType, searchTerm, jamTracks, nextPage, onNextPage }) => {
return (
<>
{selectedType && searchTerm.length && jamTracks.length > 0 ? (
<Row className="mb-2">
<Col>
<strong>
Search Results: JamTracks for {selectedType} "{searchTerm}"
</strong>
</Col>
</Row>
) : (
searchTerm.length > 0 &&
jamTracks.length > 0 && (
<Row className="mb-2">
<Col>
<strong>Search Results: JamTracks including "{searchTerm}"</strong>
</Col>
</Row>
)
)}
{jamTracks.length > 0 && (
<Row>
<Col>
<Table striped bordered className="fs--1">
<thead className="bg-200 text-900">
<tr>
<th width="30%">Song</th>
<th width="55%">Tracks</th>
<th>Shop</th>
</tr>
</thead>
<tbody>
<JamTrackPreviewProvider>
{jamTracks.map((jamTrack, index) => (
<tr key={`jamtrck-preview-row-${jamTrack.id}`}>
<td>
{jamTrack.name} by {jamTrack.original_artist}
</td>
<td>
<JKJamTrackPreview jamTrack={jamTrack} />
</td>
<td>
<JKJamTrackPurchaseButton jamTrack={jamTrack} />
</td>
</tr>
))}
</JamTrackPreviewProvider>
</tbody>
</Table>
</Col>
</Row>
)}
{nextPage && (
<Row>
<Col>
<Button color="primary" onClick={onNextPage}>
Load More
</Button>
</Col>
</Row>
)}
</>
);
};
export default JKJamTracksList;

View File

@ -0,0 +1,16 @@
import React from 'react';
const JamTrackPreviewContext = React.createContext(null)
export const JamTrackPreviewProvider = ({ children}) => {
const [currentPlayTrackId, setCurrentPlayTrackId] = React.useState(null)
return(
<JamTrackPreviewContext.Provider value={ {currentPlayTrackId, setCurrentPlayTrackId} }>
{children}
</JamTrackPreviewContext.Provider>
)
}
export const useJamTrackPreview = () => React.useContext(JamTrackPreviewContext)

View File

@ -114,6 +114,7 @@ import {
faPercentage,
faPhone,
faPlay,
faPause,
faPlug,
faPlus,
faPlusSquare,
@ -160,6 +161,8 @@ import {
faAddressCard,
faVolumeUp,
faSpinner,
faPlayCircle,
faPauseCircle
} from '@fortawesome/free-solid-svg-icons';
//import { faAcousticGuitar } from "../icons";
@ -173,6 +176,7 @@ library.add(
faHome,
faBell,
faPlay,
faPause,
faChevronRight,
faChevronLeft,
faChevronUp,
@ -288,6 +292,8 @@ library.add(
faAddressCard,
faVolumeUp,
faSpinner,
faPlayCircle,
faPauseCircle,
// Brand
faFacebook,

View File

@ -404,4 +404,15 @@ export const getJamTracks = (options = {}) => {
.then(response => resolve(response))
.catch(error => reject(error));
});
}
export const addJamtrackToShoppingCart = (options = {}) => {
return new Promise((resolve, reject) => {
apiFetch(`/shopping_carts/add_jamtrack?`, {
method: 'POST',
body: JSON.stringify(options)
})
.then(response => resolve(response))
.catch(error => reject(error));
});
}

View File

@ -11,6 +11,7 @@ import unsubscribeTranslationsEN from './locales/en/unsubscribe.json'
import profileEN from './locales/en/profile.json'
import accountEN from './locales/en/account.json'
import affiliateEN from './locales/en/affiliate.json'
import jamTracksEn from './locales/en/jamtracks.json'
import commonTranslationsES from './locales/es/common.json'
import homeTranslationsES from './locales/es/home.json'
@ -22,6 +23,7 @@ import unsubscribeTranslationsES from './locales/es/unsubscribe.json'
import profileES from './locales/es/profile.json'
import accountES from './locales/es/account.json'
import affiliateES from './locales/es/affiliate.json'
import jamTracksEs from './locales/es/jamtracks.json'
i18n.use(initReactI18next).init({
fallbackLng: 'en',
@ -37,7 +39,8 @@ i18n.use(initReactI18next).init({
profile: profileEN,
account: accountEN,
friends: friendsTranslationsEN,
affiliate: affiliateEN
affiliate: affiliateEN,
jamtracks: jamTracksEn
},
es: {
common: commonTranslationsES,
@ -49,7 +52,8 @@ i18n.use(initReactI18next).init({
profile: profileES,
account: accountES,
friends: friendsTranslationsES,
affiliate: affiliateES
affiliate: affiliateES,
jamtracks: jamTracksEs
}
},
//ns: ['translations'],

View File

@ -0,0 +1,5 @@
{
"search": {
"page_title": "Find JamTracks"
}
}

View File

@ -0,0 +1,5 @@
{
"search": {
"page_title": "encontrar pistas"
}
}

View File

@ -38,7 +38,7 @@ export const jamTrackRoutes = {
icon: 'record-vinyl',
children: [
{ to: '/jamtracks', name: 'My JamTracks'},
{ to: '/jamtracks/search', name: 'Search JamTracks'},
{ to: '/jamtracks/search', name: 'Find JamTracks'},
]
}
@ -90,6 +90,18 @@ export const helpRoutes = {
]
}
// export const cartRoutes = {
// name: 'Cart',
// to: '/cart',
// exact: true,
// icon: 'question-circle',
// children: [
// { to: '/', name: 'Shopping Cart' },
// ]
// }
export const legacyRoute = {
name: 'Legacy',
to: '/legacy',
@ -422,6 +434,7 @@ export default [
accountRoutes,
affiliateRoutes,
helpRoutes,
//cartRoutes,
//legacyRoute,
//homeRoutes,
//pageRoutes,

View File

@ -54,7 +54,8 @@ class ApiUsersController < ApiController
last_name: current_user.last_name,
name: current_user.name,
email: current_user.email,
photo_url: current_user.photo_url
photo_url: current_user.photo_url,
show_free_jamtrack: current_user.show_free_jamtrack?,
}, status: 200
end