diff options
| author | Sophie Zhang <sophie_zhang@brown.edu> | 2023-07-18 12:21:17 -0400 |
|---|---|---|
| committer | Sophie Zhang <sophie_zhang@brown.edu> | 2023-07-18 12:21:17 -0400 |
| commit | 8410cd330b676ce50948b2ec1011a72b219ee87b (patch) | |
| tree | 2009a9cd1942cf9662786fdbc2b20c6f0713cb60 /src/client/views/newlightbox/RecommendationList | |
| parent | 2bfad0eb9e3d8f8d26f66cf8e6daa801a694cab0 (diff) | |
| parent | 4e1bc2547787e9b1978c23da2045eb46407e1e3c (diff) | |
Merge branch 'master' into sophie-report-manager
Diffstat (limited to 'src/client/views/newlightbox/RecommendationList')
4 files changed, 323 insertions, 0 deletions
diff --git a/src/client/views/newlightbox/RecommendationList/RecommendationList.scss b/src/client/views/newlightbox/RecommendationList/RecommendationList.scss new file mode 100644 index 000000000..40dd47e47 --- /dev/null +++ b/src/client/views/newlightbox/RecommendationList/RecommendationList.scss @@ -0,0 +1,117 @@ +@import '../NewLightboxStyles.scss'; + +.recommendationlist-container { + height: calc(100% - 40px); + margin: 20px; + border-radius: 20px; + overflow-y: scroll; + + .recommendations { + height: fit-content; + padding: 20px; + display: flex; + flex-direction: column; + gap: 20px; + background: $gray-l1; + border-radius: 0px 0px 20px 20px; + } + + .header { + top: 0px; + position: sticky; + background: $gray-l1; + border-bottom: $standard-border; + border-color: $gray-l2; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + border-radius: 20px 20px 0px 0px; + padding: 20px; + z-index: 2; + gap: 10px; + color: $text-color-lm; + + .lb-label { + color: $gray-l3; + font-weight: $h1-weight; + font-size: $body-size; + } + + .lb-caret { + display: flex; + flex-direction: row; + justify-content: flex-end; + align-items: center; + gap: 5px; + cursor: pointer; + width: 100%; + user-select: none; + font-size: $body-size; + } + + .more { + width: 100%; + } + + &.dark { + color: $text-color-dm; + } + + .title { + height: 30px; + min-height: 30px; + font-size: $h1-size; + font-weight: $h1-weight; + text-align: left; + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + } + + .keywords { + display: flex; + flex-flow: row wrap; + gap: 5px; + + .keyword-input { + padding: 3px 7px; + background: $gray-l2; + outline: none; + border: none; + height: 21.5px; + color: $text-color-lm; + } + + .keyword { + padding: 3px 7px; + width: fit-content; + background: $gray-l2; + display: flex; + justify-content: center; + align-items: center; + flex-direction: row; + gap: 10px; + font-size: $body-size; + font-weight: $body-weight; + + &.loading { + animation: skeleton-loading-l2 1s linear infinite alternate; + min-width: 70px; + height: 21.5px; + } + } + + } + } + + &.dark { + background: $black; + } + + &.light, + &.default { + background: $gray-l1; + } +}
\ No newline at end of file diff --git a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx new file mode 100644 index 000000000..9f3c32e4e --- /dev/null +++ b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx @@ -0,0 +1,196 @@ +import { GrClose } from 'react-icons/gr'; +import { IRecommendation, Recommendation } from "../components"; +import './RecommendationList.scss'; +import * as React from 'react'; +import { IRecommendationList } from "./utils"; +import { NewLightboxView } from '../NewLightboxView'; +import { DocCast, StrCast } from '../../../../fields/Types'; +import { FaCaretDown, FaCaretUp } from 'react-icons/fa'; +import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; +import { IDocRequest, fetchKeywords, fetchRecommendations } from '../utils'; +import { IBounds } from '../ExploreView/utils'; +import { List } from '../../../../fields/List'; +import { Id } from '../../../../fields/FieldSymbols'; +import { LightboxView } from '../../LightboxView'; +import { IconButton, Size, Type } from 'browndash-components'; +import { Colors } from '../../global/globalEnums'; + +export const RecommendationList = (props: IRecommendationList) => { + const {loading, keywords} = props + const [loadingKeywords, setLoadingKeywords] = React.useState<boolean>(true) + const [showMore, setShowMore] = React.useState<boolean>(false) + const [keywordsLoc, setKeywordsLoc] = React.useState<string[]>([]) + const [update, setUpdate] = React.useState<boolean>(true) + const initialRecs: IRecommendation[] = [ + {loading: true}, + {loading: true}, + {loading: true}, + {loading: true}, + {loading: true} + ]; + const [recs, setRecs] = React.useState<IRecommendation[]>(initialRecs) + + React.useEffect(() => { + const getKeywords = async () => { + let text = StrCast(LightboxView.LightboxDoc?.text) + console.log('[1] fetching keywords') + const response = await fetchKeywords(text, 5, true) + console.log('[2] response:', response) + const kw = response.keywords; + console.log(kw); + NewLightboxView.SetKeywords(kw); + if (LightboxView.LightboxDoc) { + console.log('setting keywords on doc') + LightboxView.LightboxDoc.keywords = new List<string>(kw); + setKeywordsLoc(NewLightboxView.Keywords); + } + setLoadingKeywords(false) + } + let keywordsList = StrListCast(LightboxView.LightboxDoc!.keywords) + if (!keywordsList || keywordsList.length < 2) { + setLoadingKeywords(true) + getKeywords() + setUpdate(!update) + } else { + setKeywordsLoc(keywordsList) + setLoadingKeywords(false) + setUpdate(!update) + } + }, [NewLightboxView.LightboxDoc]) + + // terms: vannevar bush, information spaces, + React.useEffect(() => { + const getRecommendations = async () => { + console.log('fetching recommendations') + let query = 'undefined' + if (keywordsLoc) query = keywordsLoc.join(',') + let src = StrCast(NewLightboxView.LightboxDoc?.text) + let dashDocs:IDocRequest[] = []; + // get linked docs + let linkedDocs = DocListCast(NewLightboxView.LightboxDoc?.links) + console.log("linked docs", linkedDocs) + // get context docs (docs that are also in the collection) + // let contextDocs: Doc[] = DocListCast(DocCast(LightboxView.LightboxDoc?.context).data) + // let docId = LightboxView.LightboxDoc && LightboxView.LightboxDoc[Id] + // console.log("context docs", contextDocs) + // contextDocs.forEach((doc: Doc) => { + // if (docId !== doc[Id]){ + // dashDocs.push({ + // title: StrCast(doc.title), + // text: StrCast(doc.text), + // id: doc[Id], + // type: StrCast(doc.type) + // }) + // } + // }) + console.log("dash docs", dashDocs) + if (query !== undefined) { + const response = await fetchRecommendations(src, query, [], true) + const num_recs = response.num_recommendations + const recs = response.recommendations + const keywords = response.keywords + const response_bounds: IBounds = { + max_x: response.max_x, + max_y: response.max_y, + min_x: response.min_x, + min_y: response.min_y + } + // if (NewLightboxView.NewLightboxDoc) { + // NewLightboxView.NewLightboxDoc.keywords = new List<string>(keywords); + // setKeywordsLoc(NewLightboxView.Keywords); + // } + // console.log(response_bounds) + NewLightboxView.SetBounds(response_bounds) + const recommendations: IRecommendation[] = []; + for (const key in recs) { + console.log(key) + const title = recs[key].title; + const url = recs[key].url + const type = recs[key].type + const text = recs[key].text + const transcript = recs[key].transcript + const previewUrl = recs[key].previewUrl + const embedding = recs[key].embedding + const distance = recs[key].distance + const source = recs[key].source + const related_concepts = recs[key].related_concepts + const docId = recs[key].doc_id + related_concepts.length >= 1 && recommendations.push({ + title: title, + data: url, + type: type, + text: text, + transcript: transcript, + previewUrl: previewUrl, + embedding: embedding, + distance: Math.round(distance * 100) / 100, + source: source, + related_concepts: related_concepts, + docId: docId + }) + } + recommendations.sort((a, b) => { + if (a.distance && b.distance) { + return a.distance - b.distance + } else return 0 + }) + console.log("[rec]: ", recommendations) + NewLightboxView.SetRecs(recommendations) + setRecs(recommendations) + } + } + getRecommendations(); + }, [update]) + + + + return <div className={`recommendationlist-container`} onPointerDown={(e) => {e.stopPropagation()}}> + <div className={`header`}> + <div className={`title`}> + Recommendations + </div> + {NewLightboxView.LightboxDoc && <div style={{fontSize: 10}}> + The recommendations are produced based on the text in the document <b><u>{StrCast(NewLightboxView.LightboxDoc.title)}</u></b>. The following keywords are used to fetch the recommendations. + </div>} + <div className={`lb-label`}>Keywords</div> + {loadingKeywords ? <div className={`keywords`}> + <div className={`keyword ${loadingKeywords && 'loading'}`}/> + <div className={`keyword ${loadingKeywords && 'loading'}`}/> + <div className={`keyword ${loadingKeywords && 'loading'}`}/> + <div className={`keyword ${loadingKeywords && 'loading'}`}/> + </div> + : + <div className={`keywords`}> + {keywordsLoc && keywordsLoc.map((word, ind) => { + return <div className={`keyword`}> + {word} + <IconButton type={Type.PRIM} size={Size.XSMALL} color={Colors.DARK_GRAY} icon={<GrClose/>} onClick={() => { + let kw = keywordsLoc + kw.splice(ind) + NewLightboxView.SetKeywords(kw) + }}/> + </div> + })} + </div> + } + {!showMore ? + <div className={`lb-caret`} onClick={() => {setShowMore(true)}}> + More <FaCaretDown/> + </div> + : + <div className={`more`}> + <div className={`lb-caret`} onClick={() => {setShowMore(false)}}> + Less <FaCaretUp/> + </div> + <div className={`lb-label`}>Type</div> + <div className={`lb-label`}>Sources</div> + </div> + } + </div> + <div className={`recommendations`}> + {recs && recs.map((rec: IRecommendation) => { + return <Recommendation {...rec} /> + })} + </div> + </div> +}
\ No newline at end of file diff --git a/src/client/views/newlightbox/RecommendationList/index.ts b/src/client/views/newlightbox/RecommendationList/index.ts new file mode 100644 index 000000000..f4555c1f2 --- /dev/null +++ b/src/client/views/newlightbox/RecommendationList/index.ts @@ -0,0 +1 @@ +export * from './RecommendationList'
\ No newline at end of file diff --git a/src/client/views/newlightbox/RecommendationList/utils.ts b/src/client/views/newlightbox/RecommendationList/utils.ts new file mode 100644 index 000000000..cdfff3258 --- /dev/null +++ b/src/client/views/newlightbox/RecommendationList/utils.ts @@ -0,0 +1,9 @@ +import { IRecommendation } from "../components"; + +export interface IRecommendationList { + loading?: boolean, + keywords?: string[], + recs?: IRecommendation[] + getRecs?: any +} + |
