| 6 | "": "/**\\\\n * This is the main entry point for the DevHub application.\\\\n * Page route gets passed in through params, along with all other page props.\\\\n */\\\\n\\\\nconst { onDraftStateChange } = VM.require(\\\\n \\\\\\\"devhub.near/widget/devhub.entity.post.draft\\\\\\\"\\\\n);\\\\n\\\\nconst { page, ...passProps } = props;\\\\n\\\\n// Import our modules\\\\nconst { AppLayout } = VM.require(\\\\n \\\\\\\"devhub.near/widget/devhub.components.templates.AppLayout\\\\\\\"\\\\n);\\\\n\\\\nconst { CssContainer } = VM.require(\\\\\\\"devhub.near/widget/config.css\\\\\\\");\\\\nconst { Theme } = VM.require(\\\\\\\"devhub.near/widget/config.theme\\\\\\\");\\\\n\\\\nif (!AppLayout || !Theme || !CssContainer) {\\\\n return <p>Loading modules...</p>;\\\\n}\\\\n\\\\nif (!page) {\\\\n // If no page is specified, we default to the feed page TEMP\\\\n page = \\\\\\\"home\\\\\\\";\\\\n}\\\\n\\\\n// Track visits\\\\n\\\\nif (\\\\\\\"phc_es19zuLOCXpiyOGqBDkBrH7MaL77ggqJMjy8mpR1623\\\\\\\".length === 47) {\\\\n useEffect(() => {\\\\n const hashedUserId = context.accountId\\\\n ? Array.from(nacl.hash(Buffer.from(context.accountId)))\\\\n .map((b) => (\\\\\\\"00\\\\\\\" + b.toString(16)).slice(-2))\\\\n .join(\\\\\\\"\\\\\\\")\\\\n : \\\\\\\"unauthenticated\\\\\\\";\\\\n\\\\n fetch(\\\\\\\"https://eu.posthog.com/capture/\\\\\\\", {\\\\n method: \\\\\\\"POST\\\\\\\",\\\\n headers: {\\\\n \\\\\\\"content-type\\\\\\\": \\\\\\\"application/json\\\\\\\",\\\\n },\\\\n\\\\n body: JSON.stringify({\\\\n api_key: \\\\\\\"phc_es19zuLOCXpiyOGqBDkBrH7MaL77ggqJMjy8mpR1623\\\\\\\",\\\\n event: \\\\\\\"devhub_pageview\\\\\\\",\\\\n properties: {\\\\n distinct_id: hashedUserId,\\\\n page,\\\\n ...props,\\\\n },\\\\n timestamp: new Date().toISOString(),\\\\n }),\\\\n });\\\\n }, [props]);\\\\n}\\\\n\\\\n// This is our navigation, rendering the page based on the page parameter\\\\nfunction Page() {\\\\n const routes = page.split(\\\\\\\".\\\\\\\");\\\\n switch (routes[0]) {\\\\n case \\\\\\\"home\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src=\\\\\\\"devhub.near/widget/devhub.page.home\\\\\\\"\\\\n props={passProps}\\\\n />\\\\n );\\\\n }\\\\n // ?page=communities\\\\n case \\\\\\\"communities\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.page.communities\\\\\\\"}\\\\n props={passProps}\\\\n />\\\\n );\\\\n }\\\\n case \\\\\\\"announcements\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.page.announcements\\\\\\\"}\\\\n props={passProps}\\\\n />\\\\n );\\\\n }\\\\n\\\\n // ?page=community\\\\n case \\\\\\\"community\\\\\\\": {\\\\n return (\\\\n // Considering to consolidate this into a single widget,\\\\n // where each level handles its own routing.\\\\n // Modularizing a page just like we do with addons\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.entity.community.Provider\\\\\\\"}\\\\n props={{\\\\n ...passProps,\\\\n Children: (p) => {\\\\n // passing props from the Provider into the Children\\\\n switch (routes[1]) {\\\\n // ?page=community.configuration\\\\n case \\\\\\\"configuration\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\n \\\\\\\"devhub.near/widget/devhub.page.community.configuration\\\\\\\"\\\\n }\\\\n props={{\\\\n ...passProps,\\\\n ...p,\\\\n }}\\\\n />\\\\n );\\\\n }\\\\n // ?page=community\\\\n default: {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.page.community.index\\\\\\\"}\\\\n props={{\\\\n ...passProps,\\\\n ...p,\\\\n }}\\\\n />\\\\n );\\\\n }\\\\n }\\\\n },\\\\n }}\\\\n />\\\\n );\\\\n }\\\\n // ?page=create\\\\n case \\\\\\\"create\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.entity.post.PostEditor\\\\\\\"}\\\\n props={{ ...passProps, isCreatePostPage: true, onDraftStateChange }}\\\\n />\\\\n );\\\\n }\\\\n\\\\n case \\\\\\\"create-proposal\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.page.proposals\\\\\\\"}\\\\n props={{ ...passProps, instance: \\\\\\\"devhub.near\\\\\\\" }}\\\\n />\\\\n );\\\\n }\\\\n\\\\n case \\\\\\\"proposals\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.page.proposals\\\\\\\"}\\\\n props={{ ...passProps, instance: \\\\\\\"devhub.near\\\\\\\" }}\\\\n />\\\\n );\\\\n }\\\\n case \\\\\\\"proposal\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.entity.proposal.Proposal\\\\\\\"}\\\\n props={{ ...passProps, instance: \\\\\\\"devhub.near\\\\\\\" }}\\\\n />\\\\n );\\\\n }\\\\n // ?page=about\\\\n case \\\\\\\"about\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.page.about\\\\\\\"}\\\\n props={passProps}\\\\n />\\\\n );\\\\n }\\\\n case \\\\\\\"contribute\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.page.contribute\\\\\\\"}\\\\n props={passProps}\\\\n />\\\\n );\\\\n }\\\\n case \\\\\\\"profile\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.page.profile\\\\\\\"}\\\\n props={passProps}\\\\n />\\\\n );\\\\n }\\\\n // ?page=blog\\\\n case \\\\\\\"blog\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.page.blog\\\\\\\"}\\\\n props={passProps}\\\\n />\\\\n );\\\\n }\\\\n case \\\\\\\"blogv2\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.page.blogv2\\\\\\\"}\\\\n props={passProps}\\\\n />\\\\n );\\\\n }\\\\n case \\\\\\\"post\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.page.post\\\\\\\"}\\\\n props={passProps}\\\\n />\\\\n );\\\\n }\\\\n case \\\\\\\"admin\\\\\\\": {\\\\n return (\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.page.admin.index\\\\\\\"}\\\\n props={passProps}\\\\n />\\\\n );\\\\n }\\\\n default: {\\\\n return <Widget src={\\\\\\\"devhub.near/widget/devhub.page.notfound\\\\\\\"} />;\\\\n }\\\\n }\\\\n}\\\\n\\\\nreturn (\\\\n <Theme>\\\\n <CssContainer>\\\\n <AppLayout page={page}>\\\\n <Page />\\\\n </AppLayout>\\\\n </CssContainer>\\\\n </Theme>\\\\n);\\\\n" |
| 9 | "": "const BannerWrapper = styled.div`\\\\n background-image: linear-gradient(rgb(251 32 32), rgb(187 6 6));\\\\n color: white;\\\\n .text-sm {\\\\n font-size: 13px;\\\\n }\\\\n`;\\\\n\\\\nconst proposalFeedAnnouncement = (\\\\n <BannerWrapper className=\\\\\\\"d-flex gap-3 align-items-center mb-4 p-3 rounded-3\\\\\\\">\\\\n <div>\\\\n <i class=\\\\\\\"bi bi-exclamation-triangle-fill\\\\\\\"></i>\\\\n </div>\\\\n <div>\\\\n <div className=\\\\\\\"fw-bold\\\\\\\">This page is now archived! </div>\\\\n <div className=\\\\\\\"text-sm\\\\\\\">\\\\n To submit proposals, visit the\\\\n <a\\\\n href=\\\\\\\"https://nearn.io/devhub/\\\\\\\"\\\\n className=\\\\\\\"text-decoration-underline\\\\\\\"\\\\n target=\\\\\\\"_blank\\\\\\\"\\\\n rel=\\\\\\\"noopener noreferrer\\\\\\\"\\\\n >\\\\n NEARN DevHub\\\\n </a>\\\\n page to view the latest opportunities.\\\\n </div>\\\\n </div>\\\\n </BannerWrapper>\\\\n);\\\\n\\\\nconst categoryOptions = [\\\\n {\\\\n title: \\\\\\\"DevDAO Operations\\\\\\\",\\\\n value: \\\\\\\"DevDAO Operations\\\\\\\",\\\\n },\\\\n {\\\\n title: \\\\\\\"DevDAO Platform\\\\\\\",\\\\n value: \\\\\\\"DevDAO Platform\\\\\\\",\\\\n },\\\\n {\\\\n title: \\\\\\\"Events & Hackathons\\\\\\\",\\\\n value: \\\\\\\"Events & Hackathons\\\\\\\",\\\\n },\\\\n {\\\\n title: \\\\\\\"Engagement & Awareness\\\\\\\",\\\\n value: \\\\\\\"Engagement & Awareness\\\\\\\",\\\\n },\\\\n {\\\\n title: \\\\\\\"Decentralized DevRel\\\\\\\",\\\\n value: \\\\\\\"Decentralized DevRel\\\\\\\",\\\\n },\\\\n {\\\\n title: \\\\\\\"Universities & Bootcamps\\\\\\\",\\\\n value: \\\\\\\"Universities & Bootcamps\\\\\\\",\\\\n },\\\\n {\\\\n title: \\\\\\\"Tooling & Infrastructure\\\\\\\",\\\\n value: \\\\\\\"Tooling & Infrastructure\\\\\\\",\\\\n },\\\\n {\\\\n title: \\\\\\\"Other\\\\\\\",\\\\n value: \\\\\\\"Other\\\\\\\",\\\\n },\\\\n];\\\\n\\\\nreturn {\\\\n portalName: \\\\\\\"Devhub\\\\\\\",\\\\n contract: \\\\\\\"devhub.near\\\\\\\",\\\\n proposalFeedIndexerQueryName:\\\\n \\\\\\\"polyprogrammist_near_devhub_prod_v1_proposals_with_latest_snapshot\\\\\\\",\\\\n cacheUrl: \\\\\\\"https://devhub-cache-api-rs-2.fly.dev\\\\\\\",\\\\n indexerHasuraRole: \\\\\\\"polyprogrammist_near\\\\\\\",\\\\n isDevhub: true,\\\\n proposalFeedAnnouncement,\\\\n availableCategoryOptions: categoryOptions,\\\\n};\\\\n" |
| 12 | "": "const { href } = VM.require(\\\\\\\"devhub.near/widget/core.lib.url\\\\\\\") || {\\\\n href: () => {},\\\\n};\\\\n\\\\nconst instance = props.instance ?? \\\\\\\"\\\\\\\";\\\\n\\\\nconst {\\\\n contract,\\\\n rfpFeedIndexerQueryName,\\\\n proposalFeedAnnouncement,\\\\n availableCategoryOptions,\\\\n proposalFeedIndexerQueryName,\\\\n cacheUrl,\\\\n isDevhub,\\\\n isInfra,\\\\n isEvents,\\\\n} = VM.require(`${instance}/widget/config.data`);\\\\n\\\\nconst loader = (\\\\n <div className=\\\\\\\"d-flex justify-content-center align-items-center w-100\\\\\\\">\\\\n <Widget src={\\\\\\\"devhub.near/widget/devhub.components.molecule.Spinner\\\\\\\"} />\\\\n </div>\\\\n);\\\\n\\\\nif (!contract) {\\\\n return loader;\\\\n}\\\\n\\\\nfunction isNumber(v) {\\\\n return typeof v === \\\\\\\"number\\\\\\\";\\\\n}\\\\n\\\\nconst Container = styled.div`\\\\n .full-width-div {\\\\n width: 100vw;\\\\n position: relative;\\\\n left: 50%;\\\\n right: 50%;\\\\n margin-left: -50vw;\\\\n margin-right: -50vw;\\\\n }\\\\n\\\\n .card.no-border {\\\\n border-left: none !important;\\\\n border-right: none !important;\\\\n margin-bottom: -3.5rem;\\\\n }\\\\n\\\\n @media screen and (max-width: 768px) {\\\\n font-size: 13px;\\\\n }\\\\n\\\\n .text-sm {\\\\n font-size: 13px;\\\\n }\\\\n\\\\n .bg-grey {\\\\n background-color: #f4f4f4;\\\\n }\\\\n\\\\n .border-bottom {\\\\n border-bottom: 1px solid grey;\\\\n }\\\\n\\\\n .cursor-pointer {\\\\n cursor: pointer;\\\\n }\\\\n\\\\n .proposal-card {\\\\n border-left: none !important;\\\\n border-right: none !important;\\\\n border-bottom: none !important;\\\\n &:hover {\\\\n background-color: #f4f4f4;\\\\n }\\\\n }\\\\n\\\\n @media screen and (max-width: 768px) {\\\\n .theme-btn {\\\\n padding: 0.5rem 0.8rem !important;\\\\n min-height: 32px;\\\\n }\\\\n }\\\\n\\\\n a.no-space {\\\\n display: inline-block;\\\\n }\\\\n\\\\n .text-wrap {\\\\n overflow: hidden;\\\\n white-space: normal;\\\\n }\\\\n\\\\n .text-center {\\\\n text-align: center;\\\\n }\\\\n\\\\n .btn-grey-outline {\\\\n background-color: #fafafa;\\\\n border: 1px solid #e6e8eb;\\\\n color: #11181c;\\\\n\\\\n &:hover {\\\\n background-color: #e6e8eb;\\\\n }\\\\n\\\\n &:active {\\\\n border: 2px solid #e6e8eb;\\\\n }\\\\n }\\\\n`;\\\\n\\\\nconst Heading = styled.div`\\\\n font-size: 24px;\\\\n font-weight: 700;\\\\n width: 100%;\\\\n\\\\n .text-normal {\\\\n font-weight: normal !important;\\\\n }\\\\n\\\\n @media screen and (max-width: 768px) {\\\\n font-size: 18px;\\\\n }\\\\n`;\\\\n\\\\nconst FeedItem = ({ proposal, index }) => {\\\\n const accountId = proposal.author_id;\\\\n const profile = Social.get(`${accountId}/profile/**`, \\\\\\\"final\\\\\\\");\\\\n // We will have to get the proposal from the contract to get the block height.\\\\n const blockHeight = parseInt(proposal.social_db_post_block_height);\\\\n const item = {\\\\n type: \\\\\\\"social\\\\\\\",\\\\n path: `${contract}/post/main`,\\\\n blockHeight: blockHeight,\\\\n };\\\\n\\\\n const isLinked = isNumber(proposal.linked_rfp);\\\\n const rfpData = proposal.rfpData;\\\\n\\\\n return (\\\\n <a\\\\n href={href({\\\\n widgetSrc: `${instance}/widget/app`,\\\\n params: {\\\\n page: \\\\\\\"proposal\\\\\\\",\\\\n id: proposal.proposal_id,\\\\n },\\\\n })}\\\\n onClick={(e) => e.stopPropagation()}\\\\n style={{ textDecoration: \\\\\\\"none\\\\\\\" }}\\\\n >\\\\n <div\\\\n className={\\\\n \\\\\\\"proposal-card d-flex justify-content-between gap-2 text-muted cursor-pointer p-3 w-100 flex-wrap flex-sm-nowrap \\\\\\\" +\\\\n (index !== 0 && \\\\\\\" border\\\\\\\")\\\\n }\\\\n >\\\\n <div className=\\\\\\\"d-flex gap-4 w-100\\\\\\\">\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.entity.proposal.Profile\\\\\\\"}\\\\n props={{\\\\n accountId,\\\\n }}\\\\n />\\\\n <div className=\\\\\\\"d-flex flex-column gap-2 w-100 text-wrap\\\\\\\">\\\\n <div className=\\\\\\\"d-flex gap-2 align-items-center flex-wrap w-100\\\\\\\">\\\\n <div className=\\\\\\\"h6 mb-0 text-black\\\\\\\">{proposal.name}</div>\\\\n {(isInfra || isEvents) && (\\\\n <Widget\\\\n src={`devhub.near/widget/devhub.entity.proposal.MultiSelectLabelsDropdown`}\\\\n props={{\\\\n selected: proposal.labels,\\\\n disabled: true,\\\\n hideDropdown: true,\\\\n onChange: () => {},\\\\n availableOptions: availableCategoryOptions,\\\\n }}\\\\n />\\\\n )}\\\\n {isDevhub && (\\\\n <Widget\\\\n src={\\\\n \\\\\\\"devhub.near/widget/devhub.entity.proposal.CategoryTag\\\\\\\"\\\\n }\\\\n props={{\\\\n category: proposal.category,\\\\n }}\\\\n />\\\\n )}\\\\n </div>\\\\n {isLinked && rfpData && (\\\\n <div\\\\n className=\\\\\\\"text-sm text-muted d-flex gap-1 align-items-center\\\\\\\"\\\\n data-testid={\\\\n `proposalId_${proposal.proposal_id}` + `_rfpId_${rfpData.id}`\\\\n }\\\\n >\\\\n <i class=\\\\\\\"bi bi-link-45deg\\\\\\\"></i>\\\\n In response to RFP :\\\\n <a\\\\n className=\\\\\\\"text-decoration-underline flex-1\\\\\\\"\\\\n href={href({\\\\n widgetSrc: `${instance}/widget/app`,\\\\n params: {\\\\n page: \\\\\\\"rfp\\\\\\\",\\\\n id: rfpData.rfp_id,\\\\n },\\\\n })}\\\\n target=\\\\\\\"_blank\\\\\\\"\\\\n rel=\\\\\\\"noopener noreferrer\\\\\\\"\\\\n >\\\\n {rfpData.name}\\\\n </a>\\\\n </div>\\\\n )}\\\\n <div className=\\\\\\\"d-flex gap-2 align-items-center flex-wrap flex-sm-nowrap text-sm w-100\\\\\\\">\\\\n <div>#{proposal.proposal_id} \\\\u{ff65} </div>\\\\n <div className=\\\\\\\"text-truncate\\\\\\\">\\\\n By {profile.name ?? accountId} \\\\u{ff65}{\\\\\\\" \\\\\\\"}\\\\n </div>\\\\n <Widget\\\\n src=\\\\\\\"near/widget/TimeAgo\\\\\\\"\\\\n props={{\\\\n blockHeight,\\\\n blockTimestamp: proposal.timestamp,\\\\n }}\\\\n />\\\\n </div>\\\\n <div className=\\\\\\\"d-flex gap-2 align-items-center\\\\\\\">\\\\n <Widget\\\\n src=\\\\\\\"devhub.near/widget/devhub.entity.proposal.LikeButton\\\\\\\"\\\\n props={{\\\\n item,\\\\n proposalId: proposal.id,\\\\n notifyAccountId: accountId,\\\\n instance,\\\\n }}\\\\n />\\\\n\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.entity.proposal.CommentIcon\\\\\\\"}\\\\n props={{\\\\n item,\\\\n showOverlay: false,\\\\n onClick: () => {},\\\\n }}\\\\n />\\\\n </div>\\\\n </div>\\\\n </div>\\\\n <div className=\\\\\\\"align-self-center\\\\\\\" style={{ minWidth: \\\\\\\"fit-content\\\\\\\" }}>\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.entity.proposal.StatusTag\\\\\\\"}\\\\n props={{\\\\n timelineStatus: proposal.timeline.status,\\\\n }}\\\\n />\\\\n </div>\\\\n </div>\\\\n </a>\\\\n );\\\\n};\\\\n\\\\nconst getProposal = (proposal_id) => {\\\\n return Near.asyncView(contract, \\\\\\\"get_proposal\\\\\\\", {\\\\n proposal_id,\\\\n });\\\\n};\\\\n\\\\nconst getRfp = (rfp_id) => {\\\\n return Near.asyncView(contract, \\\\\\\"get_rfp\\\\\\\", {\\\\n rfp_id,\\\\n });\\\\n};\\\\n\\\\nconst FeedPage = () => {\\\\n State.init({\\\\n data: [],\\\\n author: \\\\\\\"\\\\\\\",\\\\n stage: \\\\\\\"\\\\\\\",\\\\n sort: \\\\\\\"id_desc\\\\\\\",\\\\n category: \\\\\\\"\\\\\\\",\\\\n input: \\\\\\\"\\\\\\\",\\\\n loading: false,\\\\n searchLoader: false,\\\\n makeMoreLoader: false,\\\\n aggregatedCount: null,\\\\n currentlyDisplaying: 0,\\\\n });\\\\n\\\\n const makeMoreItems = () => {\\\\n State.update({ makeMoreLoader: true });\\\\n fetchProposals(state.data.length);\\\\n };\\\\n\\\\n function searchCacheApi() {\\\\n let searchTerm = state.input;\\\\n let searchInput = encodeURI(searchTerm);\\\\n let searchUrl = `${cacheUrl}/proposals/search/${searchInput}`;\\\\n\\\\n return asyncFetch(searchUrl, {\\\\n method: \\\\\\\"GET\\\\\\\",\\\\n headers: {\\\\n accept: \\\\\\\"application/json\\\\\\\",\\\\n },\\\\n }).catch((error) => {\\\\n console.log(\\\\\\\"Error searching cache api\\\\\\\", error);\\\\n });\\\\n }\\\\n\\\\n function searchProposals() {\\\\n if (state.loading) return;\\\\n State.update({ loading: true });\\\\n\\\\n searchCacheApi().then((result) => {\\\\n let body = result.body;\\\\n\\\\n const promises = body.records.map((proposal) => {\\\\n if (isNumber(proposal.linked_rfp)) {\\\\n getRfp(proposal.linked_rfp).then((rfp) => {\\\\n return { ...proposal, rfpData: rfp };\\\\n });\\\\n } else {\\\\n return Promise.resolve(proposal);\\\\n }\\\\n });\\\\n Promise.all(promises).then((proposalsWithRfpData) => {\\\\n State.update({ aggregatedCount: body.total_records });\\\\n fetchBlockHeights(proposalsWithRfpData, 0);\\\\n });\\\\n });\\\\n }\\\\n\\\\n function fetchCacheApi(variables) {\\\\n let fetchUrl = `${cacheUrl}/proposals?order=${variables.order}&limit=${variables.limit}&offset=${variables.offset}`;\\\\n\\\\n if (variables.author_id) {\\\\n fetchUrl += `&filters.author_id=${variables.author_id}`;\\\\n }\\\\n if (variables.stage) {\\\\n fetchUrl += `&filters.stage=${variables.stage}`;\\\\n }\\\\n if (variables.category) {\\\\n if (isInfra || isEvents) {\\\\n fetchUrl += `&filters.labels=${variables.category}`;\\\\n } else {\\\\n fetchUrl += `&filters.category=${variables.category}`;\\\\n }\\\\n }\\\\n return asyncFetch(fetchUrl, {\\\\n method: \\\\\\\"GET\\\\\\\",\\\\n headers: {\\\\n accept: \\\\\\\"application/json\\\\\\\",\\\\n },\\\\n }).catch((error) => {\\\\n console.log(\\\\\\\"Error fetching cache api\\\\\\\", error);\\\\n });\\\\n }\\\\n\\\\n function fetchProposals(offset) {\\\\n if (!offset) {\\\\n offset = 0;\\\\n }\\\\n if (state.loading) return;\\\\n State.update({ loading: true });\\\\n const FETCH_LIMIT = 10;\\\\n const variables = {\\\\n order: state.sort,\\\\n limit: FETCH_LIMIT,\\\\n offset,\\\\n category: state.category ? encodeURIComponent(state.category) : \\\\\\\"\\\\\\\",\\\\n author_id: state.author ? encodeURIComponent(state.author) : \\\\\\\"\\\\\\\",\\\\n stage: state.stage ? encodeURIComponent(state.stage) : \\\\\\\"\\\\\\\",\\\\n };\\\\n fetchCacheApi(variables).then((result) => {\\\\n const body = result.body;\\\\n const promises = body.records.map((proposal) => {\\\\n if (isNumber(proposal.linked_rfp)) {\\\\n getRfp(proposal.linked_rfp).then((rfp) => {\\\\n return { ...proposal, rfpData: rfp };\\\\n });\\\\n } else {\\\\n return Promise.resolve(proposal);\\\\n }\\\\n });\\\\n Promise.all(promises).then((proposalsWithRfpData) => {\\\\n State.update({ aggregatedCount: body.total_records });\\\\n fetchBlockHeights(proposalsWithRfpData, offset);\\\\n });\\\\n });\\\\n }\\\\n\\\\n useEffect(() => {\\\\n State.update({ searchLoader: true });\\\\n fetchProposals();\\\\n }, [state.author, state.sort, state.category, state.stage]);\\\\n\\\\n const mergeItems = (newItems) => {\\\\n const items = [\\\\n ...new Set([...newItems, ...state.data].map((i) => JSON.stringify(i))),\\\\n ].map((i) => JSON.parse(i));\\\\n // Sorting in the front end\\\\n if (state.sort === \\\\\\\"id_desc\\\\\\\" || state.sort === \\\\\\\"\\\\\\\") {\\\\n items.sort((a, b) => b.proposal_id - a.proposal_id);\\\\n }\\\\n\\\\n return items;\\\\n };\\\\n\\\\n const fetchBlockHeights = (data, offset) => {\\\\n data = data.map((item, index) => ({\\\\n ...item,\\\\n timeline: JSON.parse(item.timeline),\\\\n }));\\\\n if (offset) {\\\\n let newData = mergeItems(data);\\\\n State.update({\\\\n data: newData,\\\\n currentlyDisplaying: newData.length,\\\\n loading: false,\\\\n searchLoader: false,\\\\n makeMoreLoader: false,\\\\n });\\\\n } else {\\\\n State.update({\\\\n data,\\\\n currentlyDisplaying: data.length,\\\\n loading: false,\\\\n searchLoader: false,\\\\n makeMoreLoader: false,\\\\n });\\\\n }\\\\n };\\\\n\\\\n useEffect(() => {\\\\n const handler = setTimeout(() => {\\\\n if (state.input) {\\\\n searchProposals();\\\\n } else {\\\\n fetchProposals();\\\\n }\\\\n }, 1000);\\\\n\\\\n return () => {\\\\n clearTimeout(handler);\\\\n };\\\\n }, [state.input]);\\\\n\\\\n return (\\\\n <Container className=\\\\\\\"w-100 py-4 px-2 d-flex flex-column gap-3\\\\\\\">\\\\n <div className=\\\\\\\"d-flex justify-content-between flex-wrap gap-2 align-items-center\\\\\\\">\\\\n <Heading>\\\\n Proposals\\\\n <span className=\\\\\\\"text-muted text-normal\\\\\\\">\\\\n ({state.aggregatedCount ?? state.data.length}){\\\\\\\" \\\\\\\"}\\\\n </span>\\\\n </Heading>\\\\n <div className=\\\\\\\"d-flex flex-wrap gap-4 align-items-center\\\\\\\">\\\\n <Widget\\\\n src={\\\\n \\\\\\\"devhub.near/widget/devhub.feature.proposal-search.by-input\\\\\\\"\\\\n }\\\\n props={{\\\\n search: state.input,\\\\n className: \\\\\\\"w-xs-100\\\\\\\",\\\\n onSearch: (input) => {\\\\n State.update({ input });\\\\n },\\\\n onEnter: () => {\\\\n fetchProposals();\\\\n },\\\\n }}\\\\n />\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.feature.proposal-search.by-sort\\\\\\\"}\\\\n props={{\\\\n onStateChange: (select) => {\\\\n State.update({ sort: select.value });\\\\n },\\\\n }}\\\\n />\\\\n <div className=\\\\\\\"d-flex gap-4 align-items-center\\\\\\\">\\\\n {!isInfra && (\\\\n <Widget\\\\n src={\\\\n \\\\\\\"devhub.near/widget/devhub.feature.proposal-search.by-category\\\\\\\"\\\\n }\\\\n props={{\\\\n categoryOptions: availableCategoryOptions,\\\\n onStateChange: (select) => {\\\\n State.update({ category: select.value });\\\\n },\\\\n }}\\\\n />\\\\n )}\\\\n <Widget\\\\n src={\\\\n \\\\\\\"devhub.near/widget/devhub.feature.proposal-search.by-stage\\\\\\\"\\\\n }\\\\n props={{\\\\n onStateChange: (select) => {\\\\n State.update({ stage: select.value });\\\\n },\\\\n }}\\\\n />\\\\n <Widget\\\\n src={\\\\n \\\\\\\"devhub.near/widget/devhub.feature.proposal-search.by-author\\\\\\\"\\\\n }\\\\n props={{\\\\n contract,\\\\n onAuthorChange: (select) => {\\\\n State.update({ author: select.value });\\\\n },\\\\n }}\\\\n />\\\\n </div>\\\\n </div>\\\\n <div className=\\\\\\\"mt-2 mt-xs-0\\\\\\\">\\\\n <Widget\\\\n src={\\\\\\\"devhub.near/widget/devhub.components.molecule.Button\\\\\\\"}\\\\n props={{\\\\n label: (\\\\n <div className=\\\\\\\"d-flex gap-2 align-items-center\\\\\\\">\\\\n <div>\\\\n <i class=\\\\\\\"bi bi-plus-circle-fill\\\\\\\"></i>\\\\n </div>\\\\n Submit Proposal\\\\n </div>\\\\n ),\\\\n classNames: { root: \\\\\\\"theme-btn\\\\\\\" },\\\\n disabled: true,\\\\n }}\\\\n />\\\\n </div>\\\\n </div>\\\\n <div style={{ minHeight: \\\\\\\"50vh\\\\\\\" }}>\\\\n {!Array.isArray(state.data) ? (\\\\n loader\\\\n ) : (\\\\n <div className=\\\\\\\"card no-border rounded-0 mt-4 py-3 full-width-div\\\\\\\">\\\\n <div className=\\\\\\\"container-xl\\\\\\\">\\\\n {proposalFeedAnnouncement}\\\\n <div className=\\\\\\\"mt-4 border rounded-2\\\\\\\">\\\\n {state.aggregatedCount === 0 ? (\\\\n <div class=\\\\\\\"alert alert-danger m-2\\\\\\\" role=\\\\\\\"alert\\\\\\\">\\\\n No proposals found for selected filter.{\\\\\\\" \\\\\\\"}\\\\n </div>\\\\n ) : state.searchLoader ? (\\\\n loader\\\\n ) : state.aggregatedCount > 0 ? (\\\\n state.data.map((item, index) => {\\\\n return (\\\\n <div\\\\n key={item.proposal_id}\\\\n className={\\\\n (index !== state.data.length - 1 &&\\\\n \\\\\\\"border-bottom \\\\\\\") +\\\\n index ===\\\\n 0 && \\\\\\\" rounded-top-2\\\\\\\"\\\\n }\\\\n >\\\\n <FeedItem proposal={item} index={index} />\\\\n </div>\\\\n );\\\\n })\\\\n ) : (\\\\n loader\\\\n )}\\\\n </div>\\\\n {state.aggregatedCount > 0 &&\\\\n state.aggregatedCount > state.data.length && (\\\\n <div className=\\\\\\\"my-3 container-xl\\\\\\\">\\\\n {state.makeMoreLoader ? (\\\\n loader\\\\n ) : (\\\\n <div>\\\\n {!state.loading && (\\\\n <div className=\\\\\\\"w-100\\\\\\\">\\\\n <Widget\\\\n src={`devhub.near/widget/devhub.components.molecule.Button`}\\\\n props={{\\\\n classNames: {\\\\n root: \\\\\\\"btn-grey-outline w-100 \\\\\\\",\\\\n label: \\\\\\\"text-center w-100\\\\\\\",\\\\n },\\\\n label: \\\\\\\"Load More\\\\\\\",\\\\n onClick: () => makeMoreItems(),\\\\n }}\\\\n />\\\\n </div>\\\\n )}\\\\n </div>\\\\n )}\\\\n </div>\\\\n )}\\\\n </div>\\\\n </div>\\\\n )}\\\\n </div>\\\\n </Container>\\\\n );\\\\n};\\\\n\\\\nreturn FeedPage(props);\\\\n" |