parent
24aab60556
commit
176ba1febe
File diff suppressed because it is too large
Load Diff
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
import React from 'react'
|
||||
|
||||
const JKShoppingCart = () => {
|
||||
return (
|
||||
<div>ShoppingCart</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default JKShoppingCart
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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)
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
|
@ -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'],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"search": {
|
||||
"page_title": "Find JamTracks"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"search": {
|
||||
"page_title": "encontrar pistas"
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue