wip - react components related to musician listing and filtering
This commit is contained in:
parent
eb4c327eff
commit
6c88cd1215
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
"id": "1",
|
||||
"first_name": "Test",
|
||||
"last_name": "User1",
|
||||
"name": "Test User1",
|
||||
"city": "Denver",
|
||||
"state": "CO",
|
||||
"country": "US",
|
||||
"location": "Denver, CO",
|
||||
"online": true,
|
||||
"photo_url": null,
|
||||
"musician": true,
|
||||
"gender": "M",
|
||||
"birth_date": null,
|
||||
"friend_count": 1,
|
||||
"liker_count": 0,
|
||||
"follower_count": 0,
|
||||
"following_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"biography": "Biography of Test User1",
|
||||
"favorite_count": 0,
|
||||
"audio_latency": null,
|
||||
"upcoming_session_count": 0,
|
||||
"age": null,
|
||||
"website": "www.testuser1.com",
|
||||
"skill_level": 2,
|
||||
"concert_count": 4,
|
||||
"studio_session_count": 4,
|
||||
"virtual_band": true,
|
||||
"virtual_band_commitment": 2,
|
||||
"traditional_band": true,
|
||||
"traditional_band_commitment": 4,
|
||||
"traditional_band_touring": true,
|
||||
"paid_sessions": true,
|
||||
"paid_sessions_hourly_rate": 10000,
|
||||
"paid_sessions_daily_rate": 200000,
|
||||
"free_sessions": true,
|
||||
"cowriting": true,
|
||||
"cowriting_purpose": 2,
|
||||
"subscribe_email": true,
|
||||
"is_a_teacher": false,
|
||||
"is_a_student": false,
|
||||
"online_presences": [
|
||||
{ "id": "e1962204-f652-41b0-84d6-1afd7e9172be", "service_type": "soundcloud", "username": "testuser" },
|
||||
{ "id": "005a7c78-db8b-4f72-a51f-d64d579c22b0", "service_type": "reverbnation", "username": "testuser" },
|
||||
{ "id": "2dd22eef-03ba-4743-b65b-5a194591dc86", "service_type": "bandcamp", "username": "testuser" },
|
||||
{ "id": "d6ae62b4-e1ce-4cf0-90b7-c64033533261", "service_type": "fandalism", "username": "testuser" },
|
||||
{ "id": "c6e85453-0fa9-40d0-9754-8f372d6e0ed3", "service_type": "youtube", "username": "testuser" },
|
||||
{ "id": "480ec1ad-ea1d-4990-9c68-d7f9c0174441", "service_type": "facebook", "username": "testuser" },
|
||||
{ "id": "232b26d5-c75a-4d65-9013-a07b73c8a7ae", "service_type": "twitter", "username": "testuser" }
|
||||
],
|
||||
"performance_samples": [],
|
||||
"genres": [
|
||||
{ "genre_id": "asian", "player_type": "JamRuby::User", "genre_type": "profile" },
|
||||
{ "genre_id": "classical", "player_type": "JamRuby::User", "genre_type": "profile" },
|
||||
{ "genre_id": "african", "player_type": "JamRuby::User", "genre_type": "virtual_band" },
|
||||
{ "genre_id": "classical", "player_type": "JamRuby::User", "genre_type": "virtual_band" },
|
||||
{ "genre_id": "classical", "player_type": "JamRuby::User", "genre_type": "traditional_band" },
|
||||
{ "genre_id": "blues", "player_type": "JamRuby::User", "genre_type": "free_sessions" },
|
||||
{ "genre_id": "soft rock", "player_type": "JamRuby::User", "genre_type": "free_sessions" },
|
||||
{ "genre_id": "celtic", "player_type": "JamRuby::User", "genre_type": "cowriting" },
|
||||
{ "genre_id": "tv & movie soundtrack", "player_type": "JamRuby::User", "genre_type": "cowriting" }
|
||||
],
|
||||
"bands": [],
|
||||
"instruments": [
|
||||
{ "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1, "instrument_id": "acoustic guitar" },
|
||||
{ "description": "Keyboard", "proficiency_level": 3, "priority": 8, "instrument_id": "keyboard" },
|
||||
{ "description": "Ukulele", "proficiency_level": 3, "priority": 11, "instrument_id": "ukulele" },
|
||||
{ "description": "Voice", "proficiency_level": 3, "priority": 13, "instrument_id": "voice" },
|
||||
{ "description": "Piano", "proficiency_level": 2, "priority": 10, "instrument_id": "piano" }
|
||||
],
|
||||
"is_friend": true,
|
||||
"is_following": false,
|
||||
"is_liking": false,
|
||||
"pending_friend_request": false,
|
||||
"my_audio_latency": 5,
|
||||
"internet_score": null
|
||||
}
|
||||
|
|
@ -3,32 +3,38 @@
|
|||
{
|
||||
"id": "1",
|
||||
"first_name": "Test",
|
||||
"last_name": "User",
|
||||
"name": "Test User",
|
||||
"city": "City",
|
||||
"state": "NC",
|
||||
"last_name": "User1",
|
||||
"name": "Test User1",
|
||||
"city": "Denver",
|
||||
"state": "CO",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"online": true,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"biography": "Biography of Test User1",
|
||||
"full_score": null,
|
||||
"instruments": [],
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 },
|
||||
{ "instrument_id": "keyboard", "description": "Keyboard", "proficiency_level": 3, "priority": 8 },
|
||||
{ "instrument_id": "ukulele", "description": "Ukulele", "proficiency_level": 3, "priority": 11 },
|
||||
{ "instrument_id": "voice", "description": "Voice", "proficiency_level": 3, "priority": 13 },
|
||||
{ "instrument_id": "piano", "description": "Piano", "proficiency_level": 2, "priority": 10 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_friend": true,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"friend_count": 1,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
"session_count": 10,
|
||||
"audio_latency": 5
|
||||
},
|
||||
{
|
||||
"id": "a09f9a7e-afb7-489d-870d-e13a336e0b97",
|
||||
"first_name": "Seth",
|
||||
"last_name": "Call",
|
||||
"name": "Seth Call",
|
||||
"id": "2",
|
||||
"first_name": "Test",
|
||||
"last_name": "User2",
|
||||
"name": "Test User2",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
|
|
@ -51,10 +57,10 @@
|
|||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "3dfca858-0e7c-4ad4-993a-c39421d93853",
|
||||
"first_name": "Peter",
|
||||
"last_name": "Walker",
|
||||
"name": "Peter Walker",
|
||||
"id": "3",
|
||||
"first_name": "Test",
|
||||
"last_name": "User3",
|
||||
"name": "Test User3",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
|
|
@ -77,10 +83,10 @@
|
|||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "963d5268-66b6-463a-a3ee-c97f274fc23f",
|
||||
"first_name": "Peter",
|
||||
"last_name": "Walker",
|
||||
"name": "Peter Walker",
|
||||
"id": "4",
|
||||
"first_name": "Test",
|
||||
"last_name": "User4",
|
||||
"name": "Test User4",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
|
|
@ -103,10 +109,10 @@
|
|||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "feb671a3-1821-48f0-bc14-aa26cf98bb25",
|
||||
"first_name": "David",
|
||||
"last_name": "Wilson",
|
||||
"name": "David Wilson",
|
||||
"id": "5",
|
||||
"first_name": "Test",
|
||||
"last_name": "User5",
|
||||
"name": "Test User5",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
|
|
@ -129,10 +135,379 @@
|
|||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "b1ddadd0-0263-47c4-bf91-e7767f386970",
|
||||
"first_name": "Oswald",
|
||||
"last_name": "Becca",
|
||||
"name": "Oswald Becca",
|
||||
"id": "6",
|
||||
"first_name": "Test",
|
||||
"last_name": "User6",
|
||||
"name": "Test User6",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "7",
|
||||
"first_name": "Test",
|
||||
"last_name": "User7",
|
||||
"name": "Test User7",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "8",
|
||||
"first_name": "Test",
|
||||
"last_name": "User8",
|
||||
"name": "Test User8",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "9",
|
||||
"first_name": "Test",
|
||||
"last_name": "User9",
|
||||
"name": "Test User9",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "10",
|
||||
"first_name": "Test",
|
||||
"last_name": "User10",
|
||||
"name": "Test User10",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
},
|
||||
|
||||
{
|
||||
"id": "11",
|
||||
"first_name": "Test",
|
||||
"last_name": "User11",
|
||||
"name": "Test User11",
|
||||
"city": "Denver",
|
||||
"state": "CO",
|
||||
"country": "US",
|
||||
"online": true,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "Biography of Test User1",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 },
|
||||
{ "instrument_id": "keyboard", "description": "Keyboard", "proficiency_level": 3, "priority": 8 },
|
||||
{ "instrument_id": "ukulele", "description": "Ukulele", "proficiency_level": 3, "priority": 11 },
|
||||
{ "instrument_id": "voice", "description": "Voice", "proficiency_level": 3, "priority": 13 },
|
||||
{ "instrument_id": "piano", "description": "Piano", "proficiency_level": 2, "priority": 10 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": true,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 1,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 10,
|
||||
"audio_latency": 5
|
||||
},
|
||||
{
|
||||
"id": "12",
|
||||
"first_name": "Test",
|
||||
"last_name": "User12",
|
||||
"name": "Test User12",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "13",
|
||||
"first_name": "Test",
|
||||
"last_name": "User13",
|
||||
"name": "Test User13",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "14",
|
||||
"first_name": "Test",
|
||||
"last_name": "User14",
|
||||
"name": "Test User14",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "15",
|
||||
"first_name": "Test",
|
||||
"last_name": "User15",
|
||||
"name": "Test User15",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "16",
|
||||
"first_name": "Test",
|
||||
"last_name": "User16",
|
||||
"name": "Test User16",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "17",
|
||||
"first_name": "Test",
|
||||
"last_name": "User17",
|
||||
"name": "Test User17",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "18",
|
||||
"first_name": "Test",
|
||||
"last_name": "User18",
|
||||
"name": "Test User18",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "19",
|
||||
"first_name": "Test",
|
||||
"last_name": "User19",
|
||||
"name": "Test User19",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null
|
||||
},
|
||||
{
|
||||
"id": "20",
|
||||
"first_name": "Test",
|
||||
"last_name": "User20",
|
||||
"name": "Test User20",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
|
|
@ -155,7 +530,7 @@
|
|||
"audio_latency": null
|
||||
}
|
||||
],
|
||||
"page_count": 1,
|
||||
"page_count": 2,
|
||||
"my_audio_latency": 5,
|
||||
"filter_json": "{\"id\":\"68dcc055-cb5d-40d6-8ed4-66772d1a1a31\",\"user_id\":\"27bd4a30-d1b8-4eea-8454-01a104d59381\",\"foreign_key1_id\":null,\"data_blob\":{\"sort_order\":\"latency\",\"instruments\":[],\"genres\":[],\"concert_gigs\":\"-1\",\"interests\":\"any\",\"studio_sessions\":\"-1\",\"ages\":[],\"skill_level\":\"-1\"}}",
|
||||
"description": "Current Search: Sort = Latency to Me",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
"id": "1",
|
||||
"first_name": "Test",
|
||||
"last_name": "User1",
|
||||
"name": "Test User1",
|
||||
"city": "Denver",
|
||||
"state": "CO",
|
||||
"country": "US",
|
||||
"location": "Denver, CO",
|
||||
"online": true,
|
||||
"photo_url": null,
|
||||
"musician": true,
|
||||
"gender": "M",
|
||||
"birth_date": null,
|
||||
"friend_count": 1,
|
||||
"liker_count": 0,
|
||||
"follower_count": 0,
|
||||
"following_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"biography": "Biography of Test User1",
|
||||
"favorite_count": 0,
|
||||
"audio_latency": null,
|
||||
"upcoming_session_count": 0,
|
||||
"age": null,
|
||||
"website": "www.testuser1.com",
|
||||
"skill_level": 2,
|
||||
"concert_count": 4,
|
||||
"studio_session_count": 4,
|
||||
"virtual_band": true,
|
||||
"virtual_band_commitment": 2,
|
||||
"traditional_band": true,
|
||||
"traditional_band_commitment": 4,
|
||||
"traditional_band_touring": true,
|
||||
"paid_sessions": true,
|
||||
"paid_sessions_hourly_rate": 10000,
|
||||
"paid_sessions_daily_rate": 200000,
|
||||
"free_sessions": true,
|
||||
"cowriting": true,
|
||||
"cowriting_purpose": 2,
|
||||
"subscribe_email": true,
|
||||
"is_a_teacher": false,
|
||||
"is_a_student": false,
|
||||
"online_presences": [
|
||||
{ "id": "e1962204-f652-41b0-84d6-1afd7e9172be", "service_type": "soundcloud", "username": "testuser" },
|
||||
{ "id": "005a7c78-db8b-4f72-a51f-d64d579c22b0", "service_type": "reverbnation", "username": "testuser" },
|
||||
{ "id": "2dd22eef-03ba-4743-b65b-5a194591dc86", "service_type": "bandcamp", "username": "testuser" },
|
||||
{ "id": "d6ae62b4-e1ce-4cf0-90b7-c64033533261", "service_type": "fandalism", "username": "testuser" },
|
||||
{ "id": "c6e85453-0fa9-40d0-9754-8f372d6e0ed3", "service_type": "youtube", "username": "testuser" },
|
||||
{ "id": "480ec1ad-ea1d-4990-9c68-d7f9c0174441", "service_type": "facebook", "username": "testuser" },
|
||||
{ "id": "232b26d5-c75a-4d65-9013-a07b73c8a7ae", "service_type": "twitter", "username": "testuser" }
|
||||
],
|
||||
"performance_samples": [],
|
||||
"genres": [
|
||||
{ "genre_id": "asian", "player_type": "JamRuby::User", "genre_type": "profile" },
|
||||
{ "genre_id": "classical", "player_type": "JamRuby::User", "genre_type": "profile" },
|
||||
{ "genre_id": "african", "player_type": "JamRuby::User", "genre_type": "virtual_band" },
|
||||
{ "genre_id": "classical", "player_type": "JamRuby::User", "genre_type": "virtual_band" },
|
||||
{ "genre_id": "classical", "player_type": "JamRuby::User", "genre_type": "traditional_band" },
|
||||
{ "genre_id": "blues", "player_type": "JamRuby::User", "genre_type": "free_sessions" },
|
||||
{ "genre_id": "soft rock", "player_type": "JamRuby::User", "genre_type": "free_sessions" },
|
||||
{ "genre_id": "celtic", "player_type": "JamRuby::User", "genre_type": "cowriting" },
|
||||
{ "genre_id": "tv & movie soundtrack", "player_type": "JamRuby::User", "genre_type": "cowriting" }
|
||||
],
|
||||
"bands": [],
|
||||
"instruments": [
|
||||
{ "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1, "instrument_id": "acoustic guitar" },
|
||||
{ "description": "Keyboard", "proficiency_level": 3, "priority": 8, "instrument_id": "keyboard" },
|
||||
{ "description": "Ukulele", "proficiency_level": 3, "priority": 11, "instrument_id": "ukulele" },
|
||||
{ "description": "Voice", "proficiency_level": 3, "priority": 13, "instrument_id": "voice" },
|
||||
{ "description": "Piano", "proficiency_level": 2, "priority": 10, "instrument_id": "piano" }
|
||||
],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"is_liking": false,
|
||||
"pending_friend_request": false,
|
||||
"my_audio_latency": 5,
|
||||
"internet_score": null
|
||||
}
|
||||
|
|
@ -1,35 +1,107 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import { iteratee } from "lodash"
|
||||
|
||||
describe("Friends Index page", () => {
|
||||
describe('For unauthenticated user', () => {
|
||||
describe('Friends page', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubUnauthenticate()
|
||||
cy.visit('/friends')
|
||||
})
|
||||
cy.stubAuthenticate();
|
||||
cy.intercept('POST', /\S+\/filter/, { fixture: 'people' });
|
||||
});
|
||||
|
||||
it("should not list musicians", () => {
|
||||
cy.contains("Find New Friends").should('exist')
|
||||
cy.contains("Update Search").should('exist')
|
||||
cy.contains("Reset Filters").should('exist')
|
||||
cy.get('[data-testid=peopleListTable]').should('not.exist')
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("For authenticated user", () => {
|
||||
describe('friends list', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate()
|
||||
cy.intercept('GET', '/filter', { fixture: 'people' })
|
||||
cy.visit('/friends')
|
||||
cy.visit('/friends');
|
||||
});
|
||||
|
||||
it('lists musicians', () => {
|
||||
cy.contains('Find New Friends').should('exist');
|
||||
cy.contains('Update Search').should('exist');
|
||||
cy.contains('Reset Filters').should('exist');
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.should('have.length', 20)
|
||||
.first()
|
||||
.contains('Test User1');
|
||||
});
|
||||
|
||||
//TODO: paginate
|
||||
});
|
||||
|
||||
describe('details side panel', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'person' });
|
||||
cy.visit('/friends');
|
||||
});
|
||||
|
||||
it('shows profile side panel', () => {
|
||||
//open side panel by clicking name
|
||||
cy.contains('Test User1').click();
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.should('be.visible')
|
||||
.contains('Biography of Test User1');
|
||||
closeMoreDetailsSidePanel()
|
||||
|
||||
//open side panel by clicking more button
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr').first().find('[data-testid=btnMore]').click()
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.should('be.visible')
|
||||
.contains('Biography of Test User1');
|
||||
closeMoreDetailsSidePanel()
|
||||
|
||||
//open side panel by clicking more link
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr').first().find('[data-testid=linkMore]').click()
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.should('be.visible')
|
||||
.contains('Biography of Test User1');
|
||||
closeMoreDetailsSidePanel()
|
||||
});
|
||||
});
|
||||
|
||||
describe('making friendship', () => {
|
||||
|
||||
it('add friend', () => {
|
||||
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'person' });
|
||||
cy.intercept('POST', /\S+\/friend_requests/, { statusCode: 201, body: { ok: true } });
|
||||
|
||||
cy.visit('/friends');
|
||||
cy.contains('Test User1').click();
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('be.disabled');
|
||||
});
|
||||
|
||||
it('remove friend', () => {
|
||||
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'friend' });
|
||||
cy.intercept('DELETE', /\S+\/friends\S+/, { statusCode: 204, body: { ok: true } });
|
||||
|
||||
cy.visit('/friends');
|
||||
cy.contains('Test User1').click();
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=disconnect]')
|
||||
.should('exist')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=disconnect]')
|
||||
.should('not.exist');
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('be.exist')
|
||||
.should('not.be.disabled')
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
it("should list musicians", () => {
|
||||
cy.get('[data-testid=peopleListTable]').should('exist')
|
||||
})
|
||||
})
|
||||
|
||||
describe('send message', () => {
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
function closeMoreDetailsSidePanel(){
|
||||
cy.get('[data-testid=profileSidePanel] .modal-header button.close').click()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ describe("Top Navigation", () => {
|
|||
it("shows user dropdown", () => {
|
||||
showSubscribeToUpdates()
|
||||
cy.get('[data-testid=navbarTopProfileDropdown]').should('exist')
|
||||
cy.contains("Peter Pan")
|
||||
cy.contains("My Profile")
|
||||
cy.contains("Sign out")
|
||||
cy.contains("Peter Pan").should('exist')
|
||||
cy.contains("My Profile").should('exist')
|
||||
cy.contains("Sign out").should('exist')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -18,3 +18,6 @@ import './commands'
|
|||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5906,6 +5906,15 @@
|
|||
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
||||
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
|
||||
},
|
||||
"dom7": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom7/-/dom7-3.0.0.tgz",
|
||||
"integrity": "sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ssr-window": "^3.0.0-alpha.1"
|
||||
}
|
||||
},
|
||||
"domain-browser": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
|
||||
|
|
@ -16813,6 +16822,12 @@
|
|||
"tweetnacl": "~0.14.0"
|
||||
}
|
||||
},
|
||||
"ssr-window": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz",
|
||||
"integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==",
|
||||
"dev": true
|
||||
},
|
||||
"ssri": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.1.tgz",
|
||||
|
|
@ -17293,6 +17308,16 @@
|
|||
"util.promisify": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"swiper": {
|
||||
"version": "6.8.2",
|
||||
"resolved": "https://registry.npmjs.org/swiper/-/swiper-6.8.2.tgz",
|
||||
"integrity": "sha512-VwBZ40NQ8vDzIZO9wApJTf4bDu/o3sgURMQ6fvJVSc9T63NoJV0KLC/mjkrl9GepGbmlCQNLR2tL0Kk/r8NSdw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dom7": "^3.0.0",
|
||||
"ssr-window": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"symbol-observable": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@
|
|||
"gulp-rtlcss": "^1.4.1",
|
||||
"gulp-sass": "^4.1.0",
|
||||
"gulp-sourcemaps": "^2.6.5",
|
||||
"prettier": "1.17.1"
|
||||
"prettier": "1.17.1",
|
||||
"swiper": "^6.8.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -11,7 +11,6 @@
|
|||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700|Poppins:100,200,300,400,500,600,700,800,900&display=swap"
|
||||
/>
|
||||
|
||||
<title>JamKazam</title>
|
||||
</head>
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 22 KiB |
|
|
@ -3,3 +3,10 @@
|
|||
// user-variables.scss
|
||||
//
|
||||
// Place your own variable overrides here, these will override any Bootstrap and theme variables.
|
||||
|
||||
$jk-navigation-link-color: #2c7be5 !default;
|
||||
$jk-navigation-text-color: #313336 !default;
|
||||
|
||||
$dropdown-link-color: rgba($jk-navigation-link-color, 1) !default;
|
||||
$link-color: rgba($jk-navigation-link-color, 1) !default;
|
||||
$navbar-light-color: rgba($jk-navigation-link-color, 1) !default;
|
||||
|
|
@ -4,4 +4,6 @@
|
|||
//
|
||||
// Place your own theme CSS or SCSS rules below this line, these rules will override any Bootstrap and theme variables.
|
||||
|
||||
@import './custom/user.css';
|
||||
@import './custom/nav';
|
||||
@import './custom/user';
|
||||
@import './custom/form';
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
|
||||
.form-check {
|
||||
display: block;
|
||||
min-height: 1.5rem;
|
||||
padding-left: 1.5em;
|
||||
margin-bottom: 1rem !important;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Choices */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
.choices {
|
||||
position:relative;
|
||||
margin-bottom:24px;
|
||||
font-size:16px
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
.navbar-light{
|
||||
.navbar-text {
|
||||
color: $jk-navigation-text-color;
|
||||
a {
|
||||
color: $jk-navigation-text-color;
|
||||
|
||||
@include hover-focus() {
|
||||
color: $navbar-light-active-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
--jk-good: #198754;
|
||||
--jk-fair: #e0a500;
|
||||
--jk-high: #990000;
|
||||
--jk-unknown: #8a8787;
|
||||
}
|
||||
|
||||
.nav-active .nav-link-text{
|
||||
|
|
@ -31,7 +32,13 @@
|
|||
|
||||
.latency-high {
|
||||
background-color: var(--jk-high);
|
||||
color: white;
|
||||
color: rgb(247, 239, 239);
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.latency-unknown {
|
||||
background-color: var(--jk-unknown);
|
||||
color: rgb(253, 251, 251);
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
|
|
@ -64,3 +71,35 @@
|
|||
top: 0;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.swiper-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.swiper-slide {
|
||||
// text-align: center;
|
||||
// font-size: 18px;
|
||||
// background: #fff;
|
||||
|
||||
// /* Center slide text vertically */
|
||||
// display: -webkit-box;
|
||||
// display: -ms-flexbox;
|
||||
// display: -webkit-flex;
|
||||
// display: flex;
|
||||
// -webkit-box-pack: center;
|
||||
// -ms-flex-pack: center;
|
||||
// -webkit-justify-content: center;
|
||||
// justify-content: center;
|
||||
// -webkit-box-align: center;
|
||||
// -ms-flex-align: center;
|
||||
// -webkit-align-items: center;
|
||||
// align-items: center;
|
||||
}
|
||||
|
||||
.swiper-slide img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
margin: 0;
|
||||
margin-left: auto;
|
||||
margin-right: initial;
|
||||
max-width: 350px;
|
||||
max-width: 600px;
|
||||
}
|
||||
// .modal-content {
|
||||
// border-radius: 0;
|
||||
|
|
@ -27,7 +27,8 @@
|
|||
border: none;
|
||||
border-radius: 0;
|
||||
padding: 0.5rem 1.25rem;
|
||||
background-image: linear-gradient(-45deg, #4695ff, #1970e2);
|
||||
border-bottom: solid 1px #ddd;
|
||||
//background-image: linear-gradient(-45deg, #4695ff, #1970e2);
|
||||
overflow: hidden;
|
||||
&:before,
|
||||
&:after {
|
||||
|
|
@ -36,7 +37,7 @@
|
|||
border-radius: 50%;
|
||||
height: 12.5rem;
|
||||
width: 12.5rem;
|
||||
background-image: linear-gradient(45deg, #318aff, #247cef);
|
||||
//background-image: linear-gradient(45deg, #318aff, #247cef);
|
||||
}
|
||||
&:after {
|
||||
left: 5.125rem;
|
||||
|
|
@ -51,7 +52,7 @@
|
|||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
color: white;
|
||||
color: #333;
|
||||
opacity: 0.75;
|
||||
padding-top: 0.75rem;
|
||||
margin-top: 0;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ const JKCurrentUserAvatar = () => {
|
|||
const { currentUser } = useAuth();
|
||||
|
||||
if(currentUser && currentUser.photo_url) {
|
||||
return ( <img className="avatar avatar-xl rounded-circle" src={currentUser.photo_url} /> );
|
||||
return ( <img className="avatar avatar-xl rounded-circle mr-1" src={currentUser.photo_url} /> );
|
||||
}else {
|
||||
return ( <img className="avatar avatar-xl rounded-circle" src={avatar} /> );
|
||||
return ( <img className="avatar avatar-xl rounded-circle mr-1" src={avatar} /> );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,40 +6,43 @@ import { Link } from 'react-router-dom';
|
|||
import JKCurrentUserAvatar from './JKCurrentUserAvatar'
|
||||
|
||||
const JKNavbarTopCurrentUser = () => {
|
||||
const { currentUser, setCurrentUser } = useAuth();
|
||||
const { currentUser } = useAuth();
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
|
||||
const toggle = () => setDropdownOpen(prevState => !prevState);
|
||||
|
||||
const fetchCurrentUser = () => {
|
||||
getCurrentUser()
|
||||
.then(resp => {
|
||||
if (resp.ok) {
|
||||
return resp.json();
|
||||
}
|
||||
})
|
||||
.then(data => {
|
||||
console.log('CURRENT_USER', data);
|
||||
setCurrentUser(data);
|
||||
})
|
||||
.catch(error => console.log(error));
|
||||
};
|
||||
// const fetchCurrentUser = () => {
|
||||
// getCurrentUser()
|
||||
// .then(resp => {
|
||||
// if (resp.ok) {
|
||||
// return resp.json();
|
||||
// }
|
||||
// })
|
||||
// .then(data => {
|
||||
// console.log('CURRENT_USER', data);
|
||||
// setCurrentUser(data);
|
||||
// })
|
||||
// .catch(error => console.log(error));
|
||||
// };
|
||||
|
||||
const handleLogout = () => {};
|
||||
|
||||
useEffect(() => {
|
||||
fetchCurrentUser();
|
||||
}, []);
|
||||
// useEffect(() => {
|
||||
// fetchCurrentUser();
|
||||
// }, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{currentUser &&
|
||||
<Dropdown isOpen={dropdownOpen} toggle={toggle} data-testid="navbarTopProfileDropdown">
|
||||
<DropdownToggle nav={true} caret>
|
||||
<DropdownToggle nav={true}>
|
||||
<JKCurrentUserAvatar />
|
||||
<span className="d-none d-lg-inline navbar-text">
|
||||
{currentUser.name}
|
||||
</span>
|
||||
|
||||
</DropdownToggle>
|
||||
<DropdownMenu>
|
||||
<DropdownMenu right={true}>
|
||||
<DropdownItem tag={Link} to="/pages/settings">
|
||||
My Profile
|
||||
</DropdownItem>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const Logo = ({ at, width, className, ...rest }) => {
|
|||
className={classNames(
|
||||
|
||||
{
|
||||
'align-items-center py-3': at === 'navbar-vertical',
|
||||
'align-items-center p-2': at === 'navbar-vertical',
|
||||
'align-items-center': at === 'navbar-top',
|
||||
'flex-center font-weight-extra-bold fs-5 mb-4': at === 'auth'
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import { Collapse, Navbar, NavItem, Nav } from 'reactstrap';
|
|||
import classNames from 'classnames';
|
||||
import AppContext from '../../context/Context';
|
||||
import Logo from './Logo';
|
||||
import SearchBox from './SearchBox';
|
||||
//import SearchBox from './SearchBox';
|
||||
import TopNavRightSideNavItem from './TopNavRightSideNavItem';
|
||||
import NavbarTopDropDownMenus from './NavbarTopDropDownMenus';
|
||||
//import NavbarTopDropDownMenus from './NavbarTopDropDownMenus';
|
||||
import { navbarBreakPoint, topNavbarBreakpoint } from '../../config';
|
||||
import autoCompleteInitialItem from '../../data/autocomplete/autocomplete';
|
||||
//import autoCompleteInitialItem from '../../data/autocomplete/autocomplete';
|
||||
|
||||
const NavbarTop = () => {
|
||||
const {
|
||||
|
|
@ -26,7 +26,7 @@ const NavbarTop = () => {
|
|||
return (
|
||||
<Navbar
|
||||
light
|
||||
className="navbar-glass fs--1 font-weight-semi-bold row navbar-top sticky-kit"
|
||||
className="navbar-glass fs--1 font-weight-semi-bold row navbar-top sticky-kit mb-3 d-flex"
|
||||
expand={isTopNav && topNavbarBreakpoint}
|
||||
>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ const NavbarVertical = ({ navbarStyle }) => {
|
|||
}
|
||||
}
|
||||
>
|
||||
<Nav navbar vertical>
|
||||
<Nav navbar vertical className="mt-3">
|
||||
<NavbarVerticalMenu routes={routes} />
|
||||
</Nav>
|
||||
<div className="settings px-3 px-xl-0">
|
||||
|
|
@ -101,7 +101,7 @@ const NavbarVertical = ({ navbarStyle }) => {
|
|||
</Nav>
|
||||
</div>
|
||||
)}
|
||||
<div className="navbar-vertical-divider">
|
||||
{/* <div className="navbar-vertical-divider">
|
||||
<hr className="navbar-vertical-hr my-2" />
|
||||
</div>
|
||||
<Button
|
||||
|
|
@ -114,7 +114,7 @@ const NavbarVertical = ({ navbarStyle }) => {
|
|||
className="my-3 btn-purchase"
|
||||
>
|
||||
Purchase
|
||||
</Button>
|
||||
</Button> */}
|
||||
</div>
|
||||
</Collapse>
|
||||
</Navbar>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ const NavbarVerticalMenuItem = ({ route }) => (
|
|||
<FontAwesomeIcon icon={route.icon} />
|
||||
</span>
|
||||
)}
|
||||
<span className="nav-link-text">{route.name}</span>
|
||||
<span className="nav-link-text pl-1">{route.name}</span>
|
||||
{!!route.badge && (
|
||||
<Badge color={route.badge.color || 'soft-success'} pill className="ml-2">
|
||||
{route.badge.text}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
//import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { DropdownItem, DropdownMenu, DropdownToggle, Dropdown } from 'reactstrap';
|
||||
import team3 from '../../assets/img/team/3.jpg';
|
||||
import Avatar from '../common/Avatar';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
|
||||
const ProfileDropdown = () => {
|
||||
|
|
@ -19,7 +17,7 @@ const ProfileDropdown = () => {
|
|||
// removeCookie("remember_token", {
|
||||
// domain: ".jamkazam.local"
|
||||
// });
|
||||
history.push('/authentication/basic/logout');
|
||||
// history.push('/authentication/basic/logout');
|
||||
console.log("signout...");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { Nav, NavItem, NavLink, UncontrolledTooltip } from 'reactstrap';
|
||||
import { Navbar, Nav, NavItem, NavLink, NavbarText } from 'reactstrap';
|
||||
// import ProfileDropdown from './ProfileDropdown';
|
||||
// import NotificationDropdown from './NotificationDropdown';
|
||||
// import SettingsAnimatedIcon from './SettingsAnimatedIcon';
|
||||
|
|
@ -14,32 +14,17 @@ import JKNavbarTopProfile from './JKNavbarTopProfile';
|
|||
const TopNavRightSideNavItem = () => {
|
||||
const { isTopNav, isCombo } = useContext(AppContext);
|
||||
return (
|
||||
<Nav navbar className="navbar-nav-icons ml-auto flex-row align-items-center">
|
||||
{/* <NavItem>
|
||||
<SettingsAnimatedIcon />
|
||||
</NavItem> */}
|
||||
{/* {(isCombo || isTopNav) && (
|
||||
<NavItem className={classNames(`p-2 px-lg-0 cursor-pointer`, { [`d-${navbarBreakPoint}-none`]: isCombo })}>
|
||||
<NavLink tag={Link} to="/changelog" id="changelog">
|
||||
<FontAwesomeIcon icon="code-branch" transform="right-6 grow-4" />
|
||||
</NavLink>
|
||||
<UncontrolledTooltip autohide={false} placement="left" target="changelog">
|
||||
Changelog
|
||||
</UncontrolledTooltip>
|
||||
<Navbar expand="md" className="ml-auto">
|
||||
<Nav className="align-items-center" navbar>
|
||||
<NavbarText className="d-none d-md-inline">Keep JamKazam Improving:</NavbarText>
|
||||
<NavItem className="d-none d-md-inline mr-5">
|
||||
<NavLink>Subscribe</NavLink>
|
||||
</NavItem>
|
||||
)} */}
|
||||
{/* <CartNotification /> */}
|
||||
{/* <NotificationDropdown /> */}
|
||||
|
||||
<NavItem className="me-4 d-none d-md-inline">
|
||||
<div className="navbar-text d-inline">Keep JamKazam Improving:</div>
|
||||
<NavLink className="text-green d-inline navbar-text nav-link">Subscribe</NavLink>
|
||||
</NavItem>
|
||||
|
||||
<NavItem className="nav-item me-2">
|
||||
<NavItem>
|
||||
<JKNavbarTopProfile />
|
||||
</NavItem>
|
||||
</Nav>
|
||||
</Navbar>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, {useState, useEffect} from 'react';
|
||||
import React, { useState, useEffect, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Alert, Card, CardBody, Col, Row, Button } from 'reactstrap';
|
||||
import { Alert, Card, CardBody, Col, Row, Button, Pagination, PaginationItem, PaginationLink, Form } from 'reactstrap';
|
||||
import Loader from '../common/Loader';
|
||||
import FalconCardHeader from '../common/FalconCardHeader';
|
||||
import { isIterableArray } from '../../helpers/utils';
|
||||
|
|
@ -8,9 +8,10 @@ import { isIterableArray } from '../../helpers/utils';
|
|||
// import rawPeople from '../../data/people/people';
|
||||
// import peopleCategories from '../../data/people/peopleCategories';
|
||||
// import apiFetch from '../../helpers/apiFetch';
|
||||
import JKPeopleSearch from "./JKPeopleSearch";
|
||||
import JKPeopleSearch from './JKPeopleSearch';
|
||||
import JKPeopleList from './JKPeopleList';
|
||||
import { getPeople } from "../../helpers/rest";
|
||||
import { getMusicians, getPeople } from '../../helpers/rest';
|
||||
import JKPeopleSwiper from './JKPeopleSwiper';
|
||||
|
||||
const JKPeople = ({ className }) => {
|
||||
//const { loading, data: people, setData: setPeople } = useFakeFetch(rawPeople);
|
||||
|
|
@ -18,63 +19,95 @@ const JKPeople = ({ className }) => {
|
|||
const [people, setPeople] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [showSearch, setShowSearch] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const [totalPages, setTotalPages] = useState(0);
|
||||
|
||||
const fetchPeople = React.useCallback( () => {
|
||||
getPeople()
|
||||
const fetchPeople = React.useCallback(page => {
|
||||
//getMusicians(page)
|
||||
console.log("PAGE", page);
|
||||
getPeople({ page: page })
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
//TODO: handle failure
|
||||
console.log(response);
|
||||
//console.log(response);
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('people received', data);
|
||||
setPeople(data.musicians);
|
||||
console.log('PEOPLE', data.musicians);
|
||||
//const users = new Set([...people, ...data.musicians]);
|
||||
//console.log("new users", users);
|
||||
//setPeople(Array.from(users));
|
||||
|
||||
setPeople(prev => Array.from(new Set([...prev, ...data.musicians])))
|
||||
|
||||
setTotalPages(data.page_count);
|
||||
})
|
||||
.catch(error => {
|
||||
//TODO: handle error
|
||||
console.log(error);
|
||||
}).finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, [])
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchPeople();
|
||||
}, [fetchPeople])
|
||||
fetchPeople(page);
|
||||
}, [page]);
|
||||
|
||||
const searchPeople = ({ target }) => {
|
||||
const keyword = target.value.toLowerCase();
|
||||
const filteredResult = people.filter(
|
||||
person => person.name.toLowerCase().includes(keyword) || person.institution.toLowerCase().includes(keyword)
|
||||
);
|
||||
|
||||
setPeople(keyword.length ? filteredResult : people);
|
||||
const goNextPage = () => {
|
||||
if (page < totalPages) {
|
||||
setPage(val => ++val);
|
||||
}
|
||||
};
|
||||
|
||||
const goPrevPage = () => {
|
||||
if (page > 1) {
|
||||
setPage(prev => --prev);
|
||||
}
|
||||
};
|
||||
|
||||
// const searchPeople = ({ target }) => {
|
||||
// const keyword = target.value.toLowerCase();
|
||||
// const filteredResult = people.filter(
|
||||
// person => person.name.toLowerCase().includes(keyword) || person.institution.toLowerCase().includes(keyword)
|
||||
// );
|
||||
|
||||
// setPeople(keyword.length ? filteredResult : people);
|
||||
// };
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<FalconCardHeader title="Find New Friends">
|
||||
|
||||
<div className="col-12 col-sm-auto">
|
||||
<Button color="primary" className="me-2 fs--1" onClick={() => setShowSearch(!showSearch)}>Update Search</Button>
|
||||
<Button outline disabled color="secondary" className="fs--1">Reset Filters</Button>
|
||||
</div>
|
||||
|
||||
</FalconCardHeader>
|
||||
|
||||
<JKPeopleSearch show={showSearch} setShow={setShowSearch} setPeople={setPeople} />
|
||||
<FalconCardHeader title="Find New Friends" titleClass="font-weight-bold">
|
||||
<Form inline className="mt-md-0 mt-3">
|
||||
<Button color="primary" className="me-2 mr-2 fs--1" onClick={() => setShowSearch(!showSearch)}>
|
||||
Update Search
|
||||
</Button>
|
||||
<Button outline disabled color="secondary" className="fs--1">
|
||||
Reset Filters
|
||||
</Button>
|
||||
</Form>
|
||||
</FalconCardHeader>
|
||||
|
||||
<CardBody className="pt-0">
|
||||
{loading ? (
|
||||
<Loader />
|
||||
) : isIterableArray(people) ? (
|
||||
//Start Find Friends table hidden on small screens
|
||||
<Fragment>
|
||||
<Row className="mb-3 justify-content-between d-none d-md-block">
|
||||
<div className="table-responsive-xl px-2">
|
||||
<JKPeopleList people={people} goNextPage={goNextPage} page={page} totalPages={totalPages} />
|
||||
</div>
|
||||
</Row>
|
||||
|
||||
<JKPeopleList people={people} />
|
||||
|
||||
<Row className="swiper-container d-block d-md-none">
|
||||
<JKPeopleSwiper people={people} goNextPage={goNextPage} />
|
||||
</Row>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Row className="p-card">
|
||||
<Col>
|
||||
|
|
@ -85,8 +118,9 @@ const JKPeople = ({ className }) => {
|
|||
</Row>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>);
|
||||
}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
JKPeople.propTypes = {
|
||||
className: PropTypes.string
|
||||
|
|
|
|||
|
|
@ -1,35 +1,47 @@
|
|||
import React from "react";
|
||||
import { Table } from 'reactstrap';
|
||||
import React from 'react';
|
||||
import { Table, Row, Col, Button } from 'reactstrap';
|
||||
import JKPerson from './JKPerson';
|
||||
import PropTypes from "prop-types";
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const JKPeopleList = ({people}) => {
|
||||
const JKPeopleList = ({ people, goNextPage, page, totalPages }) => {
|
||||
return (
|
||||
<Table className="table-bordered table-striped fs--1" data-testid="peopleListTable">
|
||||
<>
|
||||
<Table striped bordered className="fs--1" data-testid="peopleListTable">
|
||||
<thead className="bg-200 text-900">
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col" style={{ minWidth: 250 }}>About</th>
|
||||
<th scope="col" style={{ minWidth: 250 }}>
|
||||
About
|
||||
</th>
|
||||
<th scope="col">Instruments</th>
|
||||
<th scope="col">Genres</th>
|
||||
|
||||
<th scope="col">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="list">
|
||||
{people.map((person, index) => (
|
||||
<tr className="align-middle" key={person.id}>
|
||||
<JKPerson {...person} />
|
||||
<JKPerson person={person} viewMode="list" />
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
{page < totalPages && (
|
||||
<a className="ml-2 fw-semi-bold" href="#!" onClick={goNextPage}>
|
||||
More...
|
||||
</a>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
JKPeopleList.propTypes = {
|
||||
people: PropTypes.arrayOf(
|
||||
PropTypes.instanceOf(Object)
|
||||
)
|
||||
}
|
||||
people: PropTypes.arrayOf(PropTypes.instanceOf(Object))
|
||||
};
|
||||
|
||||
export default JKPeopleList;
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import React, { useState, useEffect, useRef, Fragment } from 'react';
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
||||
import Select from 'react-select';
|
||||
import JKTooltip from '../common/JKTooltip';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getGenres, getInstruments, postPeopleSearch } from '../../helpers/rest';
|
||||
import { getGenres, getInstruments, getPeople } from '../../helpers/rest';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
|
||||
const JKPeopleSearch = props => {
|
||||
|
|
@ -54,7 +54,7 @@ const JKPeopleSearch = props => {
|
|||
}
|
||||
})
|
||||
.then(data => {
|
||||
console.log(data);
|
||||
//console.log(data);
|
||||
setGenres(
|
||||
data.map(genre => {
|
||||
return {
|
||||
|
|
@ -88,7 +88,7 @@ const JKPeopleSearch = props => {
|
|||
}
|
||||
const updatedData = {...data, genres}
|
||||
console.log('submitting...', updatedData);
|
||||
await postPeopleSearch(updatedData)
|
||||
await getPeople(updatedData)
|
||||
.then(response => {
|
||||
if(!response.ok){
|
||||
//TODO: handle failure
|
||||
|
|
@ -121,8 +121,8 @@ const JKPeopleSearch = props => {
|
|||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Modal isOpen={show} toggle={toggle} size="xl">
|
||||
<Fragment>
|
||||
<Modal isOpen={show} toggle={toggle} className="mw-100 mx-1 mr-1 ml-1 mx-md-5 mr-md-5 ml-md-5 mx-xl-10 mr-xl-10 ml-xl-10">
|
||||
<ModalHeader toggle={toggle}>Update Search</ModalHeader>
|
||||
<ModalBody>
|
||||
<div className="px-4 pb-4">
|
||||
|
|
@ -212,7 +212,7 @@ const JKPeopleSearch = props => {
|
|||
Instruments{' '}
|
||||
<JKTooltip title="Select one or more instruments to filter for. If this field is blank, all instruments will be searched for." />
|
||||
</label>
|
||||
<div>
|
||||
<div className="choices">
|
||||
<Controller
|
||||
name="instruments"
|
||||
control={control}
|
||||
|
|
@ -225,7 +225,7 @@ const JKPeopleSearch = props => {
|
|||
Genres{' '}
|
||||
<JKTooltip title="Select one or more genres to filter for. If this field is blank, all genres will be included." />
|
||||
</label>
|
||||
<div>
|
||||
<div className="choices">
|
||||
<Controller
|
||||
name="genres"
|
||||
control={control}
|
||||
|
|
@ -237,7 +237,7 @@ const JKPeopleSearch = props => {
|
|||
<label className="form-label" htmlFor="lastActive">
|
||||
Last Active <JKTooltip title="Select onefor when the user was last active on JamKazam." />
|
||||
</label>
|
||||
<div>
|
||||
<div className="choices">
|
||||
<Controller
|
||||
name="last_active"
|
||||
control={control}
|
||||
|
|
@ -249,7 +249,7 @@ const JKPeopleSearch = props => {
|
|||
<label className="form-label" htmlFor="joined">
|
||||
Joined JamKazam <JKTooltip title="Select onefor when the user joined JamKazam." />
|
||||
</label>
|
||||
<div>
|
||||
<div className="choices">
|
||||
<Controller
|
||||
name="joined"
|
||||
control={control}
|
||||
|
|
@ -264,7 +264,7 @@ const JKPeopleSearch = props => {
|
|||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="secondary" onClick={toggle}>
|
||||
<Button color="outline-primary" onClick={toggle}>
|
||||
Cancel
|
||||
</Button>{' '}
|
||||
<Button color="primary" onClick={submitForm}>
|
||||
|
|
@ -272,7 +272,7 @@ const JKPeopleSearch = props => {
|
|||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// import Swiper core and required modules
|
||||
import SwiperCore, { Navigation, Pagination, Scrollbar, A11y } from 'swiper';
|
||||
|
||||
// Import Swiper React components
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
|
||||
// Import Swiper styles
|
||||
import 'swiper/swiper.scss';
|
||||
import 'swiper/components/navigation/navigation.scss';
|
||||
import 'swiper/components/pagination/pagination.scss';
|
||||
import 'swiper/components/scrollbar/scrollbar.scss';
|
||||
|
||||
import { Card, CardBody, CardHeader } from 'reactstrap';
|
||||
|
||||
import JKPerson from './JKPerson';
|
||||
import JKProfileAvatar from '../profile/JKProfileAvatar';
|
||||
|
||||
SwiperCore.use([Navigation, Pagination, Scrollbar, A11y]);
|
||||
|
||||
const JKPeopleSwiper = ({ people, goNextPage }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Swiper
|
||||
spaceBetween={0}
|
||||
slidesPerView={1}
|
||||
onSlideChange={() => console.log('slide change')}
|
||||
onSlideNextTransitionEnd={swiper => {
|
||||
if(swiper.isEnd){
|
||||
goNextPage()
|
||||
}
|
||||
}}
|
||||
pagination={{
|
||||
clickable: true,
|
||||
type: 'custom'
|
||||
}}
|
||||
navigation={{
|
||||
nextEl: '.swiper-button-next',
|
||||
prevEl: '.swiper-button-prev'
|
||||
}}
|
||||
>
|
||||
{people.map((person, index) => (
|
||||
<SwiperSlide key={person.id}>
|
||||
<Card>
|
||||
<CardHeader className="bg-200">
|
||||
<div className="avatar avatar-xl d-inline-block me-2 mr-2">
|
||||
<JKProfileAvatar url={person.photo_url} size="xl"/>
|
||||
</div>
|
||||
<h5 className="d-inline-block align-top mt-1">{person.name}</h5>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<JKPerson person={person} viewMode="swipe" />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
<div className="py-4 px-6 bg-white border-top w-100 fixed-bottom">
|
||||
<div className="swiper-pagination" />
|
||||
<div className="swiper-button-prev" />
|
||||
<div className="swiper-button-next" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
JKPeopleSwiper.propTypes = {
|
||||
people: PropTypes.arrayOf(PropTypes.instanceOf(Object))
|
||||
};
|
||||
|
||||
export default JKPeopleSwiper;
|
||||
|
|
@ -1,56 +1,60 @@
|
|||
import React, { Fragment, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Row, Col } from 'reactstrap';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import avatar from '../../assets/img/team/avatar.png';
|
||||
import JKProfileSidePanel from '../profile/JKProfileSidePanel';
|
||||
import JKProfileAvatar from '../profile/JKProfileAvatar';
|
||||
import JKProfileInstrumentsList from '../profile/JKProfileInstrumentsList';
|
||||
import {getUserProfile} from '../../helpers/rest';
|
||||
import { getPersonById } from '../../helpers/rest';
|
||||
import JKConnectButton from '../profile/JKConnectButton';
|
||||
import JKMessageButton from '../profile/JKMessageButton';
|
||||
import JKLatencyBadge from '../profile/JKLatencyBadge';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
|
||||
const JKPerson = ({ id, name, biography, photo_url, instruments }) => {
|
||||
const JKPerson = props => {
|
||||
const { id, name, biography, photo_url, instruments, latency_data } = props.person;
|
||||
const viewMode = props.viewMode;
|
||||
const { currentUser } = useAuth();
|
||||
|
||||
const [showSidePanel, setShowSidePanel] = useState(false)
|
||||
const [showSidePanel, setShowSidePanel] = useState(false);
|
||||
const [user, setUser] = useState(null);
|
||||
|
||||
const fetchPerson = () => {
|
||||
console.log("fetchPerson called");
|
||||
getUserProfile(id)
|
||||
const fetchPerson = async () => {
|
||||
//console.log("fetchPerson called");
|
||||
await getPersonById(id)
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
return response.json();
|
||||
} else {
|
||||
|
||||
}
|
||||
})
|
||||
.then(json => {
|
||||
console.log("USER", json);
|
||||
setUser(json)
|
||||
console.log('USER', json);
|
||||
setUser(json);
|
||||
})
|
||||
.catch(error => console.log(error))
|
||||
}
|
||||
|
||||
.catch(error => console.log(error));
|
||||
};
|
||||
|
||||
const toggleMoreDetails = () => {
|
||||
setShowSidePanel(prev => !prev)
|
||||
if(!user){
|
||||
fetchPerson()
|
||||
}
|
||||
}
|
||||
fetchPerson();
|
||||
setShowSidePanel(prev => !prev);
|
||||
};
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
{viewMode === 'list' ? (
|
||||
<>
|
||||
<td className="text-nowrap">
|
||||
<a onClick={toggleMoreDetails} className="d-flex align-items-center mb-1 fs-0">
|
||||
<div className="avatar avatar-xl">
|
||||
<JKProfileAvatar url={photo_url} />
|
||||
</div>
|
||||
<div className="ms-2">
|
||||
<div className="ml-2 ms-2">
|
||||
<strong>{name}</strong>
|
||||
</div>
|
||||
</a>
|
||||
<div>
|
||||
<strong>Latency To Me:</strong> 24ms <span className="badge latency-good">GOOD</span>
|
||||
<strong>Latency To Me:</strong>
|
||||
<JKLatencyBadge latencyData={latency_data} />
|
||||
</div>
|
||||
<div>
|
||||
<strong>Last Active:</strong> 1 hour
|
||||
|
|
@ -59,53 +63,72 @@ const JKPerson = ({ id, name, biography, photo_url, instruments }) => {
|
|||
<td>
|
||||
{biography}
|
||||
{biography.length > 0 && (
|
||||
<a onClick={toggleMoreDetails}>
|
||||
<a data-testid="linkMore" onClick={toggleMoreDetails}>
|
||||
{' '} more »
|
||||
</a>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<JKProfileInstrumentsList instruments={instruments} />
|
||||
</td>
|
||||
<td><JKProfileInstrumentsList instruments={instruments} /></td>
|
||||
|
||||
<td className="text-nowrap">
|
||||
<a
|
||||
href="#"
|
||||
className="btn fs--1 btn-primary px-2 py-1 mr-1"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
title="Connect with This Friend"
|
||||
>
|
||||
<FontAwesomeIcon icon="plus" transform="shrink-4 down-1" className="mr-1" />
|
||||
</a>
|
||||
<JKConnectButton
|
||||
currentUser={currentUser}
|
||||
user={props.person}
|
||||
addContent={<FontAwesomeIcon icon="plus" transform="shrink-4 down-1" className="mr-1" />}
|
||||
removeContent={<FontAwesomeIcon icon="minus" transform="shrink-4 down-1" className="mr-1" />}
|
||||
cssClasses="fs--1 px-2 py-1 mr-1"
|
||||
/>
|
||||
|
||||
<a
|
||||
href="#"
|
||||
className="btn btn-primary fs--1 px-2 py-1 mr-1"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
title="Send a Message"
|
||||
>
|
||||
<JKMessageButton currentUser={currentUser} user={props.person} cssClasses="fs--1 px-2 py-1 mr-1">
|
||||
<FontAwesomeIcon icon="comments" transform="shrink-4 down-1" className="mr-1" />
|
||||
</a>
|
||||
</JKMessageButton>
|
||||
|
||||
<a onClick={toggleMoreDetails}>
|
||||
<a onClick={toggleMoreDetails} data-testid="btnMore">
|
||||
<span className="btn btn-primary fs--1 px-2 py-1" data-bs-toggle="tooltip" title="View Profile">
|
||||
<FontAwesomeIcon icon="user" transform="shrink-4 down-1" className="mr-1" />
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
<strong>Latency To Me:</strong> <JKLatencyBadge latencyData={latency_data} />
|
||||
</div>
|
||||
<div>
|
||||
<strong>Last Active:</strong> 1 hour
|
||||
</div>
|
||||
|
||||
<h5>Instruments</h5>
|
||||
<JKProfileInstrumentsList instruments={instruments} />
|
||||
|
||||
<JKConnectButton
|
||||
currentUser={currentUser}
|
||||
user={props.person}
|
||||
addContent={<FontAwesomeIcon icon="plus" transform="shrink-4 down-1" className="mr-1" />}
|
||||
removeContent={<FontAwesomeIcon icon="minus" transform="shrink-4 down-1" className="mr-1" />}
|
||||
cssClasses="fs--1 px-2 py-1 mr-1"
|
||||
/>
|
||||
|
||||
<JKMessageButton currentUser={currentUser} user={props.person} cssClasses="fs--1 px-2 py-1 mr-1">
|
||||
<FontAwesomeIcon icon="comments" transform="shrink-4 down-1" className="mr-1" />
|
||||
</JKMessageButton>
|
||||
|
||||
<a onClick={toggleMoreDetails} data-testid="btnMore">
|
||||
<span className="btn btn-primary fs--1 px-2 py-1" data-bs-toggle="tooltip" title="View Profile">
|
||||
<FontAwesomeIcon icon="user" transform="shrink-4 down-1" className="mr-1" />
|
||||
</span>
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
<JKProfileSidePanel user={user} show={showSidePanel} setShow={setShowSidePanel} />
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
JKPerson.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
biography: PropTypes.string.isRequired,
|
||||
photo_url: PropTypes.string,
|
||||
person: PropTypes.object.isRequired,
|
||||
viewMode: PropTypes.string
|
||||
//instruments: PropTypes.arrayOf(PropTypes.string)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import {addFriend as connect, removeFriend as disconnect} from '../../helpers/rest';
|
||||
|
||||
const JKConnectButton = (props) => {
|
||||
const { user, currentUser, addContent, removeContent, cssClasses } = props
|
||||
const [isFriend, setIsFriend] = useState(false)
|
||||
const [pendingFriendRequest, setPendingFriendRequest] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setIsFriend(user.is_friend);
|
||||
setPendingFriendRequest(user.pending_friend_request)
|
||||
}, [user])
|
||||
|
||||
const addFriend = () => {
|
||||
connect(currentUser.id, user.id)
|
||||
.then(resp => {
|
||||
if(resp.ok && resp.status === 201){
|
||||
setPendingFriendRequest(true)
|
||||
}
|
||||
})
|
||||
.catch(err => console.log(err))
|
||||
}
|
||||
|
||||
const removeFriend = () => {
|
||||
disconnect(currentUser.id, user.id)
|
||||
.then(resp => {
|
||||
if(resp.ok){
|
||||
setIsFriend(false)
|
||||
}
|
||||
})
|
||||
.catch(err => console.log(err))
|
||||
}
|
||||
|
||||
const buttonTitle = () => {
|
||||
let title;
|
||||
if (pendingFriendRequest) {
|
||||
title = 'You have sent a friend request to this user';
|
||||
} else if (!isFriend) {
|
||||
title = 'Send friend request';
|
||||
} else if (isFriend) {
|
||||
title = 'Unfriend this person';
|
||||
}
|
||||
return title;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{ !isFriend ? (
|
||||
<button className={`btn btn-primary ${cssClasses}`} data-testid="connect" disabled={pendingFriendRequest} onClick={addFriend} title={buttonTitle()}>
|
||||
{addContent}
|
||||
</button>)
|
||||
: (
|
||||
<button className={`btn btn-primary ${cssClasses}`} data-testid="disconnect" onClick={removeFriend} title={buttonTitle()}>
|
||||
{removeContent}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default JKConnectButton;
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const JKLatencyBadge = ({ latencyData, showAll }) => {
|
||||
let label = 'UNKNOWN';
|
||||
let latency = '';
|
||||
if (latencyData) {
|
||||
label = latencyData.label;
|
||||
if (showAll) {
|
||||
latency = `${latencyData.ars_internet_latency}ms + ${latencyData.audio_latency}ms`;
|
||||
} else {
|
||||
latency = `${latencyData.ars_total_latency}ms`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{latency} <span className={`badge latency-${label.toLowerCase()}`}>{label}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
JKLatencyBadge.propTypes = {
|
||||
latencyData: PropTypes.object,
|
||||
showAll: PropTypes.bool
|
||||
};
|
||||
|
||||
JKLatencyBadge.defaultProps = {
|
||||
showAll: false
|
||||
};
|
||||
|
||||
export default JKLatencyBadge;
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import JKMessageModal from './JKMessageModal';
|
||||
|
||||
const JKMessageButton = props => {
|
||||
const { currentUser, user, cssClasses, children } = props;
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [isFriend, setIsFriend] = useState(false);
|
||||
const [pendingFriendRequest, setPendingFriendRequest] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsFriend(user.is_friend);
|
||||
setPendingFriendRequest(user.pending_friend_request);
|
||||
}, [user]);
|
||||
|
||||
const buttonTitle = () => {
|
||||
return isFriend ? 'Send friend request' : 'You can message this user once you are friends.'
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<JKMessageModal show={showModal} setShow={setShowModal} user={user} currentUser={currentUser} />
|
||||
<button
|
||||
onClick={() => setShowModal(!showModal)}
|
||||
className={`btn btn-primary ${cssClasses}`}
|
||||
title={buttonTitle()}
|
||||
data-testid="message"
|
||||
disabled={!isFriend}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default JKMessageButton;
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal, ModalHeader, ModalBody, Row, Col, Button, ModalFooter } from 'reactstrap';
|
||||
import { Scrollbar } from 'react-scrollbars-custom';
|
||||
import JKProfileAvatar from './JKProfileAvatar';
|
||||
import { getTextMessages, createTextMessage } from '../../helpers/rest';
|
||||
|
||||
const JKMessageModal = props => {
|
||||
const { show, setShow, user } = props;
|
||||
const [offset, setOffset] = useState(0);
|
||||
const [messages, setMessages] = useState([]);
|
||||
const [newMessage, setNewMessage] = useState("");
|
||||
const toggle = () => setShow(!show);
|
||||
const LIMIT = 20;
|
||||
|
||||
const fetchMessages = async () => {
|
||||
await getTextMessages({
|
||||
target_user_id: user.id,
|
||||
offset: offset,
|
||||
limit: LIMIT
|
||||
})
|
||||
.then(resp => {
|
||||
if (resp.ok) {
|
||||
return resp.json();
|
||||
} else {
|
||||
}
|
||||
})
|
||||
.then(json => {
|
||||
console.log(json);
|
||||
setMessages(json);
|
||||
})
|
||||
.catch(error => console.log(error));
|
||||
}
|
||||
|
||||
const sendMessage = () => {
|
||||
const params = { message: newMessage, target_user_id: user.id }
|
||||
console.log("Sending new message", params);
|
||||
createTextMessage(params)
|
||||
.then(resp => console.log(resp))
|
||||
.catch(error => console.log(error))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
console.log('JKMessageModal User', user.id);
|
||||
fetchMessages();
|
||||
}
|
||||
}, [show]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal isOpen={show} toggle={toggle}>
|
||||
<ModalHeader toggle={toggle}>Conversation with {user.name}</ModalHeader>
|
||||
<ModalBody>
|
||||
<Scrollbar style={{ width: '100%', height: 400 }}>
|
||||
{messages.map((message, index) => (
|
||||
<div className="d-flex mb-2 mr-1" key={message.id}>
|
||||
<div className="avatar avatar-2xl d-inline-block me-2 mr-2">
|
||||
<JKProfileAvatar url={user.photo_url} />
|
||||
</div>
|
||||
|
||||
<div className="d-inline-block ml-2 ms-2 mb-0">
|
||||
<p className="mb-0">{message.message}</p>
|
||||
<time className="notification-time">{message.created_at}</time>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Scrollbar>
|
||||
<Row>
|
||||
<Col>
|
||||
<textarea style={{ width: '100%' }} value={newMessage} onChange={(e) => setNewMessage(e.target.value) } />
|
||||
</Col>
|
||||
</Row>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onClick={toggle}>Close</Button>
|
||||
<Button color="primary" onClick={sendMessage}>Send</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default JKMessageModal;
|
||||
|
|
@ -1,15 +1,27 @@
|
|||
import React from "react";
|
||||
import avatar from "../../assets/img/team/avatar.png";
|
||||
|
||||
const JKProfileAvatar = ({url}) => {
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import defaultAvatarUrl from '../../assets/img/team/avatar.png';
|
||||
import Avatar from '../common/Avatar';
|
||||
|
||||
const JKProfileAvatar = ({ url, size }) => {
|
||||
const avatarUrl = () => {
|
||||
if (url) {
|
||||
return ( <img className="avatar avatar-xl rounded-circle" src={url} /> );
|
||||
return url;
|
||||
} else {
|
||||
return ( <img className="avatar avatar-xl rounded-circle" src={avatar} /> );
|
||||
return defaultAvatarUrl;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
return <Avatar src={avatarUrl()} size={size} />;
|
||||
};
|
||||
|
||||
JKProfileAvatar.propTypes = {
|
||||
url: PropTypes.string,
|
||||
size: PropTypes.string
|
||||
};
|
||||
|
||||
JKProfileAvatar.defaultProps = {
|
||||
size: 'l'
|
||||
};
|
||||
|
||||
export default JKProfileAvatar;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Modal, ModalBody, ModalHeader } from 'reactstrap';
|
||||
import ScrollBarCustom from '../common/ScrollBarCustom';
|
||||
|
|
@ -11,27 +11,31 @@ import JKProfileOnlinePresence from './JKProfileOnlinePresence';
|
|||
import JKProfileInterests from './JKProfileInterests';
|
||||
import JKProfileGenres from './JKProfileGenres';
|
||||
import JKProfilePerformanceSamples from './JKProfilePerformanceSamples';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import JKConnectButton from './JKConnectButton';
|
||||
import JKLatencyBadge from './JKLatencyBadge';
|
||||
|
||||
const JKProfileSidePanel = props => {
|
||||
const { show, setShow, user } = props;
|
||||
|
||||
const {currentUser} = useAuth()
|
||||
const toggle = () => setShow(!show);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={show}
|
||||
toggle={toggle}
|
||||
modalClassName="overflow-hidden modal-fixed-right modal-theme"
|
||||
modalClassName="overflow-hidden modal-profile modal-fixed-right w-100 modal-theme"
|
||||
className="modal-dialog-vertical"
|
||||
contentClassName="vh-100 border-0"
|
||||
data-testid="profileSidePanel"
|
||||
>
|
||||
<ModalHeader tag="div" toggle={toggle} className="modal-header-settings">
|
||||
{user && (
|
||||
<Fragment>
|
||||
<div className="avatar avatar-2xl d-inline-block me-2">
|
||||
<JKProfileAvatar url={user.photo_url} />
|
||||
<div className="avatar avatar-2xl d-inline-block me-2 mr-2">
|
||||
<JKProfileAvatar url={user.photo_url} size="2xl" />
|
||||
</div>
|
||||
<h4 className="d-inline-block align-middle mt-1">{user.name}</h4>
|
||||
<h4 className="d-inline-block align-middle mt-n3 pt-0">{user.name}</h4>
|
||||
</Fragment>
|
||||
)}
|
||||
</ModalHeader>
|
||||
|
|
@ -43,12 +47,11 @@ const JKProfileSidePanel = props => {
|
|||
)
|
||||
}}
|
||||
>
|
||||
<ModalBody>
|
||||
<ModalBody className="pb-5">
|
||||
{user && (
|
||||
<div>
|
||||
<p>
|
||||
<strong>Latency to Me:</strong> 18ms Internet + 8ms Audio{' '}
|
||||
<span className="badge latency-good">GOOD</span>
|
||||
<strong>Latency to Me:</strong> <JKLatencyBadge latencyData={user.latencyData} showAll={true} />
|
||||
<br />
|
||||
<strong>Location:</strong> {`${user.city}, ${user.country}`}
|
||||
<br />
|
||||
|
|
@ -115,14 +118,16 @@ const JKProfileSidePanel = props => {
|
|||
<h5>Interests</h5>
|
||||
<JKProfileInterests user={user} />
|
||||
|
||||
{ currentUser &&
|
||||
|
||||
<div className="p-3 bg-white border-top fixed-bottom">
|
||||
<button className="btn btn-primary">
|
||||
<span className="fas fa-plus" /> Add Friend
|
||||
</button>{' '}
|
||||
<button className="btn btn-outline-primary">
|
||||
<JKConnectButton currentUser={currentUser} user={user} addContent={<><FontAwesomeIcon icon="plus" transform="shrink-4 down-1" className="mr-1" /> Add Friend </>} removeContent={<><FontAwesomeIcon icon="minus" transform="shrink-4 down-1" className="mr-1" /> Disconnect</>} />
|
||||
{' '}
|
||||
<button className="btn btn-outline-primary" data-testid="message">
|
||||
<span className="fas fa-comment" /> Send Message
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</ModalBody>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,22 @@
|
|||
import { reject } from "lodash";
|
||||
import apiFetch from "./apiFetch";
|
||||
|
||||
export const getPeople = () => {
|
||||
export const getMusicians = (page) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
apiFetch("/search/musicians?results=true")
|
||||
apiFetch(`/search/musicians?results=true`)
|
||||
.then(response => resolve(response))
|
||||
.catch(error => reject(error))
|
||||
})
|
||||
}
|
||||
|
||||
export const getUserProfile = (id) => {
|
||||
// export const getPeople = (page) => {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// apiFetch(`/filter?page=${page}`)
|
||||
// .then(response => resolve(response))
|
||||
// .catch(error => reject(error))
|
||||
// })
|
||||
// }
|
||||
|
||||
export const getPersonById = (id) => {
|
||||
return new Promise((resolve, reject) => (
|
||||
apiFetch(`/users/${id}/profile?show_teacher=true`)
|
||||
.then(response => resolve(response))
|
||||
|
|
@ -17,9 +24,9 @@ export const getUserProfile = (id) => {
|
|||
))
|
||||
}
|
||||
|
||||
export const postPeopleSearch = (data) => {
|
||||
export const getPeople = ({ data, page } = {}) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
apiFetch("/filter", {
|
||||
apiFetch(`/filter?page=${page}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
|
|
@ -51,3 +58,43 @@ export const getCurrentUser = () => {
|
|||
.catch(error => reject(error))
|
||||
})
|
||||
}
|
||||
|
||||
export const addFriend = (userId, friendId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
apiFetch(`/users/${userId}/friend_requests`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ friend_id: friendId })
|
||||
})
|
||||
.then(response => resolve(response))
|
||||
.catch(error => reject(error))
|
||||
})
|
||||
}
|
||||
|
||||
export const removeFriend = (userId, friendId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
apiFetch(`/users/${userId}/friends/${friendId}`, {
|
||||
method: 'DELETE'
|
||||
})
|
||||
.then(response => resolve(response))
|
||||
.catch(error => reject(error))
|
||||
})
|
||||
}
|
||||
|
||||
export const getTextMessages = (options = {}) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
apiFetch(`/text_messages?${new URLSearchParams(options)}`)
|
||||
.then(response => resolve(response))
|
||||
.catch(error => reject(error))
|
||||
})
|
||||
}
|
||||
|
||||
export const createTextMessage = (options) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
apiFetch(`/text_messages`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(options)
|
||||
})
|
||||
.then(response => resolve(response))
|
||||
.catch(error => reject(error))
|
||||
})
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import React, { useContext, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Route, Switch, Redirect } from 'react-router-dom';
|
||||
|
||||
import Dashboard from '../components/dashboard/Dashboard';
|
||||
import { Route, Switch, Redirect, NavLink } from 'react-router-dom';
|
||||
import { Card, CardBody, Row, Col, Button } from "reactstrap";
|
||||
import Logo from '../components/navbar/Logo';
|
||||
import Section from '../components/common/Section';
|
||||
//import Dashboard from '../components/dashboard/Dashboard';
|
||||
import JKDashboard from '../components/dashboard/JkDashboard';
|
||||
//import DashboardAlt from '../components/dashboard-alt/DashboardAlt';
|
||||
|
||||
|
|
@ -17,6 +19,7 @@ import ProductProvider from '../components/e-commerce/ProductProvider';
|
|||
import { getPageName } from '../helpers/utils';
|
||||
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import { getCurrentUser } from '../helpers/rest';
|
||||
|
||||
const DashboardRoutes = loadable(() => import('./DashboardRoutes'));
|
||||
|
||||
|
|
@ -25,6 +28,26 @@ const DashboardLayout = ({ location }) => {
|
|||
|
||||
const isKanban = getPageName('kanban');
|
||||
|
||||
const {currentUser, setCurrentUser} = useAuth()
|
||||
|
||||
const fetchCurrentUser = () => {
|
||||
getCurrentUser()
|
||||
.then(resp => {
|
||||
if (resp.ok) {
|
||||
return resp.json();
|
||||
}
|
||||
})
|
||||
.then(data => {
|
||||
console.log('layout CURRENT_USER', data);
|
||||
setCurrentUser(data);
|
||||
})
|
||||
.catch(error => console.log(error));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchCurrentUser()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
DashboardRoutes.preload();
|
||||
}, []);
|
||||
|
|
@ -34,6 +57,26 @@ const DashboardLayout = ({ location }) => {
|
|||
}, [location.pathname]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{ currentUser == null ? (
|
||||
<Section className="py-0">
|
||||
<Row className="flex-center min-vh-100 py-6">
|
||||
<Col sm={10} md={8} lg={6} xl={5} className="col-xxl-4">
|
||||
<Logo/>
|
||||
<Card>
|
||||
<CardBody className="fs--1 font-weight-normal p-5">
|
||||
<Row className="justify-content-center">
|
||||
<h3 className="mt-3 mt-md-4 font-weight-normal fs-2">Signin to begin</h3>
|
||||
<p>Please login to your jamkazam account before accessing this interface.</p>
|
||||
<a className="btn btn-primary" href="https://www.jamkazam.com/signin">Signin</a>
|
||||
</Row>
|
||||
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</Section>
|
||||
) : (
|
||||
<div className={isFluid || isKanban ? 'container-fluid' : 'container'}>
|
||||
{isVertical && <NavbarVertical isKanban={isKanban} navbarStyle={navbarStyle} />}
|
||||
<ProductProvider>
|
||||
|
|
@ -51,6 +94,9 @@ const DashboardLayout = ({ location }) => {
|
|||
{/* <SidePanelModal path={location.pathname} /> */}
|
||||
</ProductProvider>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
|
||||
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
|
||||
import { toast, ToastContainer } from 'react-toastify';
|
||||
import { CloseButton, Fade } from '../components/common/Toast';
|
||||
|
||||
import DashboardLayout from './DashboardLayout';
|
||||
import ErrorLayout from './ErrorLayout';
|
||||
|
||||
import loadable from '@loadable/component';
|
||||
|
||||
|
||||
const AuthBasicLayout = loadable(() => import('./AuthBasicLayout'));
|
||||
// import loadable from '@loadable/component';
|
||||
|
||||
//const AuthBasicLayout = loadable(() => import('./AuthBasicLayout'));
|
||||
//const Landing = loadable(() => import('../components/landing/Landing'));
|
||||
//const WizardLayout = loadable(() => import('../components/auth/wizard/WizardLayout'));
|
||||
//const AuthCardRoutes = loadable(() => import('../components/auth/card/AuthCardRoutes'));
|
||||
|
|
@ -17,31 +18,16 @@ const AuthBasicLayout = loadable(() => import('./AuthBasicLayout'));
|
|||
|
||||
const Layout = () => {
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
AuthBasicLayout.preload();
|
||||
//AuthBasicLayout.preload();
|
||||
//Landing.preload();
|
||||
//WizardLayout.preload();
|
||||
//AuthCardRoutes.preload();
|
||||
//AuthSplitRoutes.preload();
|
||||
}, []);
|
||||
|
||||
// async function fetchUserData(){
|
||||
// await apiFetch('/users/current_user_data')
|
||||
// .then(resp => {
|
||||
// if(!resp.ok){
|
||||
// //handle error
|
||||
// console.log("fetchUserData failed", resp);
|
||||
// }else{
|
||||
// return resp.json()
|
||||
// }
|
||||
// })
|
||||
// .then(json => {
|
||||
// setCurrentUser(json)
|
||||
// console.log("USER>>>>>>>", json);
|
||||
// })
|
||||
// .catch(error => console.log(error))
|
||||
// }
|
||||
|
||||
return (
|
||||
<Router fallback={<span />}>
|
||||
<Switch>
|
||||
|
|
@ -51,10 +37,9 @@ const Layout = () => {
|
|||
<Route path="/authentication/split" component={AuthSplitRoutes} />
|
||||
<Route path="/authentication/wizard" component={WizardLayout} /> */}
|
||||
<Route path="/errors" component={ErrorLayout} />
|
||||
<Route path="/authentication/basic" component={AuthBasicLayout} />
|
||||
{/* <Route path="/authentication/basic" component={AuthBasicLayout} /> */}
|
||||
|
||||
<Route component={DashboardLayout} />
|
||||
|
||||
</Switch>
|
||||
|
||||
<ToastContainer transition={Fade} closeButton={<CloseButton />} position={toast.POSITION.BOTTOM_LEFT} />
|
||||
|
|
@ -62,6 +47,4 @@ const Layout = () => {
|
|||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
export default Layout;
|
||||
|
|
|
|||
|
|
@ -43,3 +43,4 @@ public/uploads
|
|||
/log/*.out
|
||||
BUILD_NUMBER
|
||||
.byebug_history
|
||||
.ruby-version
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ class ApiSearchController < ApiController
|
|||
|
||||
respond_to :json
|
||||
|
||||
include LatencyHelper
|
||||
|
||||
def index
|
||||
if 1 == params[Search::PARAM_MUSICIAN].to_i || 1 == params[Search::PARAM_BAND].to_i
|
||||
query = params.clone
|
||||
|
|
@ -93,8 +95,8 @@ class ApiSearchController < ApiController
|
|||
end
|
||||
end
|
||||
|
||||
#filter users by first fetching users from latency graph database
|
||||
#for the latency filter options and then quering the relational
|
||||
#Filter users by first fetching users from latency graph database
|
||||
#for latency specific filter options and then query the postgresql relational
|
||||
#database for other filter options
|
||||
def filter
|
||||
latency_good = ActiveRecord::Type::Boolean.new.type_cast_from_user(params[:latency_good])
|
||||
|
|
@ -102,7 +104,9 @@ class ApiSearchController < ApiController
|
|||
latency_high = ActiveRecord::Type::Boolean.new.type_cast_from_user(params[:latency_high])
|
||||
|
||||
begin
|
||||
user_ids = user_ids_by_latency(latency_good, latency_fair, latency_high)
|
||||
@latency_data = users_latency_data(latency_good, latency_fair, latency_high)
|
||||
#debugger
|
||||
user_ids = @latency_data.map{ |l_data| l_data[:user_id] }
|
||||
|
||||
filter_params = {
|
||||
"sort_order"=>"latency",
|
||||
|
|
@ -168,58 +172,73 @@ private
|
|||
"#{Rails.application.config.latency_data_host}/search_users"
|
||||
end
|
||||
|
||||
def user_ids_by_latency(latency_good, latency_fair, latency_high)
|
||||
user_ids = []
|
||||
if latency_good || latency_fair || latency_high
|
||||
uri = URI(filter_latency_url)
|
||||
begin
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = true if Rails.application.config.latency_data_host.start_with?("https://")
|
||||
req = Net::HTTP::Post.new(uri)
|
||||
req["Authorization"] = "Basic #{Rails.application.config.latency_data_host_auth_code}"
|
||||
req["Content-Type"] = "application/json"
|
||||
req.body = {
|
||||
my_user_id: current_user.id,
|
||||
my_public_ip: request.remote_ip,
|
||||
my_device_id: nil,
|
||||
my_client_id: nil
|
||||
}.to_json
|
||||
# def users_latency_data(latency_good, latency_fair, latency_high)
|
||||
# latency_data = []
|
||||
# if latency_good || latency_fair || latency_high
|
||||
# uri = URI(filter_latency_url)
|
||||
# begin
|
||||
# http = Net::HTTP.new(uri.host, uri.port)
|
||||
# http.use_ssl = true if Rails.application.config.latency_data_host.start_with?("https://")
|
||||
# req = Net::HTTP::Post.new(uri)
|
||||
# req["Authorization"] = "Basic #{Rails.application.config.latency_data_host_auth_code}"
|
||||
# req["Content-Type"] = "application/json"
|
||||
# req.body = {
|
||||
# my_user_id: current_user.id,
|
||||
# my_public_ip: request.remote_ip,
|
||||
# my_device_id: nil,
|
||||
# my_client_id: nil
|
||||
# }.to_json
|
||||
|
||||
response = http.request(req)
|
||||
# response = http.request(req)
|
||||
|
||||
if response.is_a?(Net::HTTPOK) || response.is_a?(Net::HTTPSuccess)
|
||||
graph_db_users = JSON.parse(response.body)["users"]
|
||||
if latency_good || latency_fair || latency_high
|
||||
graph_db_users.select! do |user|
|
||||
total_latency = user["ars"]["total_latency"].to_f
|
||||
(total_latency <= 40 && latency_good) ||
|
||||
(total_latency > 40 && total_latency <= 80 && latency_fair) ||
|
||||
(total_latency > 80 && latency_high)
|
||||
end
|
||||
end
|
||||
# if response.is_a?(Net::HTTPOK) || response.is_a?(Net::HTTPSuccess)
|
||||
# graph_db_users = JSON.parse(response.body)["users"]
|
||||
# if latency_good || latency_fair || latency_high
|
||||
# graph_db_users.select! do |user|
|
||||
# total_latency = user["ars"]["total_latency"].to_f
|
||||
# (total_latency >= 0 && total_latency <= 40 && latency_good) ||
|
||||
# (total_latency > 40 && total_latency <= 80 && latency_fair) ||
|
||||
# (total_latency > 80 && latency_high)
|
||||
# end
|
||||
# end
|
||||
|
||||
user_ids = graph_db_users.map { | user | user["user_id"] }.uniq
|
||||
|
||||
return user_ids
|
||||
else
|
||||
logger.debug("Latency response failed: #{response}")
|
||||
Bugsnag.notify("LatencyResponseFailed") do |report|
|
||||
report.severity = "faliure"
|
||||
report.add_tab(:latency, {
|
||||
user_id: current_user.id,
|
||||
name: current_user.name,
|
||||
params: params,
|
||||
url: filter_latency_url,
|
||||
code: response.code,
|
||||
body: response.body,
|
||||
})
|
||||
end
|
||||
end
|
||||
rescue => exception
|
||||
raise exception
|
||||
end
|
||||
end
|
||||
user_ids
|
||||
end
|
||||
# latency_data = graph_db_users.map { | user |
|
||||
# total = user["ars"]["total_latency"].to_f
|
||||
# label = if total >= 0 && total <= 40
|
||||
# 'good'
|
||||
# elsif total > 40 && total <= 80
|
||||
# 'fair'
|
||||
# else
|
||||
# 'high'
|
||||
# end
|
||||
# {
|
||||
# user_id: user["user_id"],
|
||||
# audio_latency: user["audio_latency"].to_f,
|
||||
# ars_total_latency: user["ars"]["total_latency"].to_f,
|
||||
# ars_internet_latency: user["ars"]["internet_latency"].to_f
|
||||
# }
|
||||
# }.uniq
|
||||
# #debugger
|
||||
# return latency_data
|
||||
# else
|
||||
# logger.debug("Latency response failed: #{response}")
|
||||
# Bugsnag.notify("LatencyResponseFailed") do |report|
|
||||
# report.severity = "faliure"
|
||||
# report.add_tab(:latency, {
|
||||
# user_id: current_user.id,
|
||||
# name: current_user.name,
|
||||
# params: params,
|
||||
# url: filter_latency_url,
|
||||
# code: response.code,
|
||||
# body: response.body,
|
||||
# })
|
||||
# end
|
||||
# end
|
||||
# rescue => exception
|
||||
# raise exception
|
||||
# end
|
||||
# end
|
||||
# latency_data
|
||||
# end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class ApiUsersController < ApiController
|
|||
end
|
||||
|
||||
def me
|
||||
render json: { first_name: current_user.first_name, last_name: current_user.last_name, name: current_user.name, photo_url: current_user.photo_url }, status: 200
|
||||
render json: { id: current_user.id, first_name: current_user.first_name, last_name: current_user.last_name, name: current_user.name, photo_url: current_user.photo_url }, status: 200
|
||||
end
|
||||
|
||||
def show
|
||||
|
|
|
|||
|
|
@ -0,0 +1,153 @@
|
|||
module LatencyHelper
|
||||
LATENCY_SCORES = {
|
||||
good: { label: 'GOOD', min: 0, max: 40 },
|
||||
fair: { label: 'FAIR', min: 40, max: 80 },
|
||||
high: { label: 'HIGH', min: 80, max: 10000000 },
|
||||
me: { label: 'ME', min: -1, max: -1 },
|
||||
unknown: { label: 'UNKNOWN', min: -2, max: -2 }
|
||||
}
|
||||
# def users_latency_data(latency_good, latency_fair, latency_high)
|
||||
# latency_data = []
|
||||
# if latency_good || latency_fair || latency_high
|
||||
# uri = URI(filter_latency_url)
|
||||
# begin
|
||||
# http = Net::HTTP.new(uri.host, uri.port)
|
||||
# http.use_ssl = true if Rails.application.config.latency_data_host.start_with?("https://")
|
||||
# req = Net::HTTP::Post.new(uri)
|
||||
# req["Authorization"] = "Basic #{Rails.application.config.latency_data_host_auth_code}"
|
||||
# req["Content-Type"] = "application/json"
|
||||
# req.body = {
|
||||
# my_user_id: current_user.id,
|
||||
# my_public_ip: request.remote_ip,
|
||||
# my_device_id: nil,
|
||||
# my_client_id: nil
|
||||
# }.to_json
|
||||
|
||||
# response = http.request(req)
|
||||
|
||||
# if response.is_a?(Net::HTTPOK) || response.is_a?(Net::HTTPSuccess)
|
||||
# graph_db_users = JSON.parse(response.body)["users"]
|
||||
# if latency_good || latency_fair || latency_high
|
||||
# graph_db_users.select! do |user|
|
||||
# total_latency = user["ars"]["total_latency"].to_f
|
||||
# (total_latency >= LATENCY_SCORES[:good][:min] && total_latency <= LATENCY_SCORES[:good][:max] && latency_good) ||
|
||||
# (total_latency > LATENCY_SCORES[:fair][:min] && total_latency <= LATENCY_SCORES[:fair][:max] && latency_fair) ||
|
||||
# (total_latency > LATENCY_SCORES[:high][:min] && latency_high)
|
||||
# end
|
||||
# end
|
||||
|
||||
# latency_data = graph_db_users.map { | user |
|
||||
# total = user["ars"]["total_latency"].to_f
|
||||
# label = if total >= LATENCY_SCORES[:good][:min] && total <= LATENCY_SCORES[:good][:max]
|
||||
# LATENCY_SCORES[:good][:label]
|
||||
# elsif total > LATENCY_SCORES[:fair][:min] && total <= LATENCY_SCORES[:fair][:max]
|
||||
# LATENCY_SCORES[:fair][:label]
|
||||
# elsif total > LATENCY_SCORES[:high][:min]
|
||||
# LATENCY_SCORES[:high][:label]
|
||||
# else
|
||||
# LATENCY_SCORES[:unknown][:label]
|
||||
# end
|
||||
# {
|
||||
# user_id: user["user_id"],
|
||||
# audio_latency: user["audio_latency"].to_f,
|
||||
# ars_total_latency: user["ars"]["total_latency"].to_f,
|
||||
# ars_internet_latency: user["ars"]["internet_latency"].to_f,
|
||||
# label: label
|
||||
# }
|
||||
# }.uniq
|
||||
# #debugger
|
||||
# return latency_data
|
||||
# else
|
||||
# logger.debug("Latency response failed: #{response}")
|
||||
# Bugsnag.notify("LatencyResponseFailed") do |report|
|
||||
# report.severity = "faliure"
|
||||
# report.add_tab(:latency, {
|
||||
# user_id: current_user.id,
|
||||
# name: current_user.name,
|
||||
# params: params,
|
||||
# url: filter_latency_url,
|
||||
# code: response.code,
|
||||
# body: response.body,
|
||||
# })
|
||||
# end
|
||||
# end
|
||||
# rescue => exception
|
||||
# raise exception
|
||||
# end
|
||||
# end
|
||||
# latency_data
|
||||
# end
|
||||
|
||||
|
||||
def users_latency_data(latency_good, latency_fair, latency_high)
|
||||
latency_data = []
|
||||
if latency_good || latency_fair || latency_high
|
||||
uri = URI(filter_latency_url)
|
||||
begin
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = true if Rails.application.config.latency_data_host.start_with?("https://")
|
||||
req = Net::HTTP::Post.new(uri)
|
||||
req["Authorization"] = "Basic #{Rails.application.config.latency_data_host_auth_code}"
|
||||
req["Content-Type"] = "application/json"
|
||||
req.body = {
|
||||
my_user_id: current_user.id,
|
||||
my_public_ip: request.remote_ip,
|
||||
my_device_id: nil,
|
||||
my_client_id: nil
|
||||
}.to_json
|
||||
|
||||
response = http.request(req)
|
||||
|
||||
if response.is_a?(Net::HTTPOK) || response.is_a?(Net::HTTPSuccess)
|
||||
graph_db_users = JSON.parse(response.body)["users"]
|
||||
if latency_good || latency_fair || latency_high
|
||||
graph_db_users.select! do |user|
|
||||
total_latency = user["ars"]["total_latency"].to_f
|
||||
(total_latency >= LATENCY_SCORES[:good][:min] && total_latency <= LATENCY_SCORES[:good][:max] && latency_good) ||
|
||||
(total_latency > LATENCY_SCORES[:fair][:min] && total_latency <= LATENCY_SCORES[:fair][:max] && latency_fair) ||
|
||||
(total_latency > LATENCY_SCORES[:high][:min] && latency_high)
|
||||
end
|
||||
end
|
||||
|
||||
latency_data = graph_db_users.map { | user |
|
||||
total = user["ars"]["total_latency"].to_f
|
||||
label = if total >= LATENCY_SCORES[:good][:min] && total <= LATENCY_SCORES[:good][:max]
|
||||
LATENCY_SCORES[:good][:label]
|
||||
elsif total > LATENCY_SCORES[:fair][:min] && total <= LATENCY_SCORES[:fair][:max]
|
||||
LATENCY_SCORES[:fair][:label]
|
||||
elsif total > LATENCY_SCORES[:high][:min]
|
||||
LATENCY_SCORES[:high][:label]
|
||||
else
|
||||
LATENCY_SCORES[:unknown][:label]
|
||||
end
|
||||
{
|
||||
user_id: user["user_id"],
|
||||
audio_latency: user["audio_latency"].to_f,
|
||||
ars_total_latency: user["ars"]["total_latency"].to_f,
|
||||
ars_internet_latency: user["ars"]["internet_latency"].to_f,
|
||||
label: label
|
||||
}
|
||||
}.uniq
|
||||
return latency_data
|
||||
else
|
||||
logger.debug("Latency response failed: #{response}")
|
||||
Bugsnag.notify("LatencyResponseFailed") do |report|
|
||||
report.severity = "faliure"
|
||||
report.add_tab(:latency, {
|
||||
user_id: current_user.id,
|
||||
name: current_user.name,
|
||||
params: params,
|
||||
url: filter_latency_url,
|
||||
code: response.code,
|
||||
body: response.body,
|
||||
})
|
||||
end
|
||||
end
|
||||
rescue => exception
|
||||
raise exception
|
||||
end
|
||||
end
|
||||
latency_data
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -60,6 +60,17 @@ if @search.is_a?(BaseSearch)
|
|||
node :audio_latency do |musician|
|
||||
last_jam_audio_latency(musician)
|
||||
end
|
||||
|
||||
node :latency_data do |musician|
|
||||
|
||||
if latency = @latency_data.detect{|l_data| l_data[:user_id] == musician.id }
|
||||
{
|
||||
audio_latency: latency[:audio_latency],
|
||||
ars_internet_latency: latency[:ars_internet_latency],
|
||||
ars_total_latency: latency[:ars_total_latency]
|
||||
}
|
||||
end if @latency_data
|
||||
end
|
||||
}
|
||||
|
||||
elsif @search.is_a?(BandSearch)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Rails.application.config.middleware.insert_before 0, Rack::Cors do
|
|||
|
||||
resource '*',
|
||||
headers: :any,
|
||||
methods: [:get, :post, :options],
|
||||
methods: [:get, :post, :delete, :options],
|
||||
credentials: true
|
||||
end
|
||||
end
|
||||
|
|
@ -10,16 +10,18 @@ describe "Musician Filter API", type: :request do
|
|||
let(:user4) { FactoryGirl.create(:user) }
|
||||
let(:user5) { FactoryGirl.create(:user) }
|
||||
let(:user6) { FactoryGirl.create(:user) }
|
||||
let(:user7) { FactoryGirl.create(:user) }
|
||||
|
||||
let(:latency_data_uri) { /\S+\/search_users/ }
|
||||
|
||||
let(:response_body) { mock_latency_response([
|
||||
{ user: user1, ars_total_latency: 1.0, ars_internet_latency: 0.4, audio_latency: 0.6 }, #GOOD
|
||||
{ user: user2, ars_total_latency: 40.0, ars_internet_latency: 25.0, audio_latency: 15.0 }, #GOOD
|
||||
{ user: user3, ars_total_latency: 41.0, ars_internet_latency: 25, audio_latency: 16 }, #FAIR
|
||||
{ user: user3, ars_total_latency: 40.1, ars_internet_latency: 25, audio_latency: 15.1 }, #FAIR
|
||||
{ user: user4, ars_total_latency: 80.0, ars_internet_latency: 40, audio_latency: 40.0 }, #FAIR
|
||||
{ user: user5, ars_total_latency: 81.0, ars_internet_latency: 41, audio_latency: 40 }, #HIGH
|
||||
{ user: user6, ars_total_latency: 100.0, ars_internet_latency: 50.0, audio_latency: 50.0 } #HIGH
|
||||
{ user: user5, ars_total_latency: 80.1, ars_internet_latency: 40.1, audio_latency: 40 }, #HIGH
|
||||
{ user: user6, ars_total_latency: 100.0, ars_internet_latency: 50.0, audio_latency: 50.0 }, #HIGH
|
||||
{ user: user7, ars_total_latency: -2, ars_internet_latency: -1, audio_latency: -1 } #UNKNOWN
|
||||
])
|
||||
}
|
||||
|
||||
|
|
@ -28,7 +30,6 @@ describe "Musician Filter API", type: :request do
|
|||
let(:rock) { Genre.find_by_id('rock') }
|
||||
|
||||
before(:each) do
|
||||
#ActiveMusicSession.delete_all
|
||||
User.delete_all
|
||||
stub_request(:post, latency_data_uri)
|
||||
.with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'})
|
||||
|
|
@ -45,20 +46,27 @@ describe "Musician Filter API", type: :request do
|
|||
|
||||
it "get all musicians" do
|
||||
get '/api/search/musicians.json?results=true'
|
||||
expect(JSON.parse(response.body)["musicians"].size).to eq(6)
|
||||
expect(JSON.parse(response.body)["musicians"].size).to eq(7)
|
||||
end
|
||||
|
||||
it "filter all musicians for all latency types" do
|
||||
post '/api/filter.json', { latency_good: false, latency_fair: false, latency_high: false }
|
||||
expect(JSON.parse(response.body)["musicians"].size).to eq(6)
|
||||
expect(JSON.parse(response.body)["musicians"].size).to eq(7)
|
||||
expect(JSON.parse(response.body)["musicians"][0]["latency_data"]).to eq(nil)
|
||||
end
|
||||
|
||||
it "filter GOOD latency users" do
|
||||
it "filter GOOD latency users", focus: true do
|
||||
post '/api/filter.json', { latency_good: true, latency_fair: false, latency_high: false }
|
||||
expect(response.content_type).to eq("application/json")
|
||||
expect(response).to render_template(:index)
|
||||
expect(response).to have_http_status(:created)
|
||||
expect(JSON.parse(response.body)["musicians"].size).to eq(2)
|
||||
|
||||
#test latency data
|
||||
expect(JSON.parse(response.body)["musicians"][0]["latency_data"]).not_to eq(nil)
|
||||
expect(JSON.parse(response.body)["musicians"][0]["latency_data"]["audio_latency"]).not_to eq(nil)
|
||||
expect(JSON.parse(response.body)["musicians"][0]["latency_data"]["ars_internet_latency"]).not_to eq(nil)
|
||||
expect(JSON.parse(response.body)["musicians"][0]["latency_data"]["ars_total_latency"]).not_to eq(nil)
|
||||
end
|
||||
|
||||
it "filter FAIR latency musicians" do
|
||||
|
|
@ -120,7 +128,7 @@ describe "Musician Filter API", type: :request do
|
|||
|
||||
end
|
||||
|
||||
it "filter musicians by instruments they play", focus: true do
|
||||
it "filter musicians by instruments they play" do
|
||||
user1.musician_instruments << FactoryGirl.create(:musician_instrument, player: user1, instrument: JamRuby::Instrument.find('drums'), proficiency_level: 1 )
|
||||
user1.musician_instruments << FactoryGirl.create(:musician_instrument, player: user1, instrument: JamRuby::Instrument.find('violin'), proficiency_level: 2 )
|
||||
user1.save!
|
||||
|
|
@ -144,7 +152,7 @@ describe "Musician Filter API", type: :request do
|
|||
expect(JSON.parse(response.body)["musicians"].size).to eq(0)
|
||||
end
|
||||
|
||||
it "filter musicians by joined within day" do
|
||||
it "filter musicians by days ago that they joined" do
|
||||
user1.created_at = 1.day.ago
|
||||
user1.save!
|
||||
|
||||
|
|
|
|||
|
|
@ -26,3 +26,4 @@ target
|
|||
vendor
|
||||
BUILD_NUMBER
|
||||
.byebug_history
|
||||
.ruby-version
|
||||
|
|
|
|||
Loading…
Reference in New Issue